diff --git a/.pylintrc b/.pylintrc index b9e432bf9..2de22da43 100644 --- a/.pylintrc +++ b/.pylintrc @@ -74,7 +74,8 @@ disable=no-self-use, # disabled as it is too verbose unnecessary-pass, # allow for methods with just "pass", for clarity no-else-return, # relax "elif" after a clause with a return docstring-first-line-empty, # relax docstring style - import-outside-toplevel + import-outside-toplevel, + bad-continuation, bad-whitespace # differences of opinion with black @@ -215,7 +216,7 @@ max-nested-blocks=5 [FORMAT] # Maximum number of characters on a single line. -max-line-length=100 +max-line-length=105 # Regexp for a line that is allowed to be longer than the limit. ignore-long-lines=^\s*(# )??$ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b80d57716..bd2f6dc4c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,6 +33,10 @@ please ensure that: make style ``` from the root of the Optimization repository clone for lint and style conformance checks. + + If your code fails the local style checks (specifically the black + code formatting check) you can use `make black` to automatically + fix update the code formatting. For unit testing please see [Testing](#testing) section below. diff --git a/Makefile b/Makefile index 9c607717f..e9cbb7e91 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,10 @@ mypy: mypy qiskit_optimization test tools style: - pycodestyle qiskit_optimization test tools + black --check qiskit_optimization test tools + +black: + black qiskit_optimization test tools test: python -m unittest discover -v test diff --git a/qiskit_optimization/__init__.py b/qiskit_optimization/__init__.py index d0e8480dc..e3ed1ebce 100644 --- a/qiskit_optimization/__init__.py +++ b/qiskit_optimization/__init__.py @@ -87,8 +87,4 @@ from .exceptions import QiskitOptimizationError from .problems.quadratic_program import QuadraticProgram -__all__ = ['__version__', - 'QuadraticProgram', - 'QiskitOptimizationError', - 'INFINITY' - ] +__all__ = ["__version__", "QuadraticProgram", "QiskitOptimizationError", "INFINITY"] diff --git a/qiskit_optimization/algorithms/__init__.py b/qiskit_optimization/algorithms/__init__.py index a47367387..1705c65a7 100644 --- a/qiskit_optimization/algorithms/__init__.py +++ b/qiskit_optimization/algorithms/__init__.py @@ -62,29 +62,64 @@ """ -from .admm_optimizer import ADMMOptimizer, ADMMOptimizationResult, ADMMState, ADMMParameters +from .admm_optimizer import ( + ADMMOptimizer, + ADMMOptimizationResult, + ADMMState, + ADMMParameters, +) from .cobyla_optimizer import CobylaOptimizer from .cplex_optimizer import CplexOptimizer -from .goemans_williamson_optimizer import (GoemansWilliamsonOptimizer, - GoemansWilliamsonOptimizationResult) +from .goemans_williamson_optimizer import ( + GoemansWilliamsonOptimizer, + GoemansWilliamsonOptimizationResult, +) from .grover_optimizer import GroverOptimizer, GroverOptimizationResult -from .minimum_eigen_optimizer import (MinimumEigenOptimizer, MinimumEigenOptimizationResult) +from .minimum_eigen_optimizer import ( + MinimumEigenOptimizer, + MinimumEigenOptimizationResult, +) from .multistart_optimizer import MultiStartOptimizer -from .optimization_algorithm import (OptimizationAlgorithm, OptimizationResult, - OptimizationResultStatus, SolutionSample) -from .recursive_minimum_eigen_optimizer import (RecursiveMinimumEigenOptimizer, - RecursiveMinimumEigenOptimizationResult, - IntermediateResult) +from .optimization_algorithm import ( + OptimizationAlgorithm, + OptimizationResult, + OptimizationResultStatus, + SolutionSample, +) +from .recursive_minimum_eigen_optimizer import ( + RecursiveMinimumEigenOptimizer, + RecursiveMinimumEigenOptimizationResult, + IntermediateResult, +) from .slsqp_optimizer import SlsqpOptimizer, SlsqpOptimizationResult -from .warm_start_qaoa_optimizer import (BaseAggregator, MeanAggregator, WarmStartQAOAFactory, - WarmStartQAOAOptimizer) +from .warm_start_qaoa_optimizer import ( + BaseAggregator, + MeanAggregator, + WarmStartQAOAFactory, + WarmStartQAOAOptimizer, +) -__all__ = ["ADMMOptimizer", "OptimizationAlgorithm", "OptimizationResult", - "OptimizationResultStatus", "BaseAggregator", - "CplexOptimizer", "CobylaOptimizer", "GoemansWilliamsonOptimizer", - "GoemansWilliamsonOptimizationResult", "GroverOptimizer", "GroverOptimizationResult", - "MeanAggregator", - "MinimumEigenOptimizer", "MinimumEigenOptimizationResult", - "RecursiveMinimumEigenOptimizer", "RecursiveMinimumEigenOptimizationResult", - "IntermediateResult", "SlsqpOptimizer", "SlsqpOptimizationResult", "SolutionSample", - "WarmStartQAOAOptimizer", "WarmStartQAOAFactory"] +__all__ = [ + "ADMMOptimizer", + "OptimizationAlgorithm", + "OptimizationResult", + "OptimizationResultStatus", + "BaseAggregator", + "CplexOptimizer", + "CobylaOptimizer", + "GoemansWilliamsonOptimizer", + "GoemansWilliamsonOptimizationResult", + "GroverOptimizer", + "GroverOptimizationResult", + "MeanAggregator", + "MinimumEigenOptimizer", + "MinimumEigenOptimizationResult", + "RecursiveMinimumEigenOptimizer", + "RecursiveMinimumEigenOptimizationResult", + "IntermediateResult", + "SlsqpOptimizer", + "SlsqpOptimizationResult", + "SolutionSample", + "WarmStartQAOAOptimizer", + "WarmStartQAOAFactory", +] diff --git a/qiskit_optimization/algorithms/admm_optimizer.py b/qiskit_optimization/algorithms/admm_optimizer.py index 0d0779a1a..a70af31ff 100644 --- a/qiskit_optimization/algorithms/admm_optimizer.py +++ b/qiskit_optimization/algorithms/admm_optimizer.py @@ -21,8 +21,11 @@ from qiskit.algorithms import NumPyMinimumEigensolver from .minimum_eigen_optimizer import MinimumEigenOptimizer -from .optimization_algorithm import (OptimizationResultStatus, OptimizationAlgorithm, - OptimizationResult) +from .optimization_algorithm import ( + OptimizationResultStatus, + OptimizationAlgorithm, + OptimizationResult, +) from .slsqp_optimizer import SlsqpOptimizer from ..problems.constraint import Constraint from ..problems.linear_constraint import LinearConstraint @@ -39,21 +42,23 @@ class ADMMParameters: """Defines a set of parameters for ADMM optimizer.""" - def __init__(self, - rho_initial: float = 10000, - factor_c: float = 100000, - beta: float = 1000, - maxiter: int = 10, - tol: float = 1.e-4, - max_time: float = np.inf, - three_block: bool = True, - vary_rho: int = UPDATE_RHO_BY_TEN_PERCENT, - tau_incr: float = 2, - tau_decr: float = 2, - mu_res: float = 10, - mu_merit: float = 1000, - warm_start: bool = False, - max_iter: Optional[int] = None) -> None: + def __init__( + self, + rho_initial: float = 10000, + factor_c: float = 100000, + beta: float = 1000, + maxiter: int = 10, + tol: float = 1.0e-4, + max_time: float = np.inf, + three_block: bool = True, + vary_rho: int = UPDATE_RHO_BY_TEN_PERCENT, + tau_incr: float = 2, + 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. Args: @@ -85,10 +90,12 @@ def __init__(self, """ super().__init__() if max_iter is not None: - warnings.warn('The max_iter parameter is deprecated as of ' - '0.8.0 and will be removed no sooner than 3 months after the release. ' - 'You should use maxiter instead.', - DeprecationWarning) + warnings.warn( + "The max_iter parameter is deprecated as of " + "0.8.0 and will be removed no sooner than 3 months after the release. " + "You should use maxiter instead.", + DeprecationWarning, + ) maxiter = max_iter self.mu_merit = mu_merit self.mu_res = mu_res @@ -105,7 +112,9 @@ def __init__(self, self.warm_start = warm_start def __repr__(self) -> str: - props = ", ".join(["{}={}".format(key, value) for (key, value) in vars(self).items()]) + props = ", ".join( + ["{}={}".format(key, value) for (key, value) in vars(self).items()] + ) return "{0}({1})".format(type(self).__name__, props) @@ -117,9 +126,7 @@ class ADMMState: optimization problem. State is returned as the third value. """ - def __init__(self, - op: QuadraticProgram, - rho_initial: float) -> None: + def __init__(self, op: QuadraticProgram, rho_initial: float) -> None: """ Args: op: The optimization problem being solved. @@ -178,10 +185,16 @@ def __init__(self, class ADMMOptimizationResult(OptimizationResult): - """ ADMMOptimization Result.""" + """ADMMOptimization Result.""" - def __init__(self, x: np.ndarray, fval: float, variables: List[Variable], - state: ADMMState, status: OptimizationResultStatus) -> None: + def __init__( + self, + x: np.ndarray, + fval: float, + variables: List[Variable], + state: ADMMState, + status: OptimizationResultStatus, + ) -> None: """ Args: x: the optimal value found by ADMM. @@ -190,11 +203,13 @@ def __init__(self, x: np.ndarray, fval: float, variables: List[Variable], state: the internal computation state of ADMM. status: Termination status of an optimization algorithm """ - super().__init__(x=x, fval=fval, variables=variables, status=status, raw_results=state) + super().__init__( + x=x, fval=fval, variables=variables, status=status, raw_results=state + ) @property def state(self) -> ADMMState: - """ returns state """ + """returns state""" return self._raw_results @@ -209,9 +224,12 @@ class ADMMOptimizer(OptimizationAlgorithm): Optimization on Classical and Quantum Computers. arXiv preprint arXiv:2001.02069. """ - def __init__(self, qubo_optimizer: Optional[OptimizationAlgorithm] = None, - continuous_optimizer: Optional[OptimizationAlgorithm] = None, - params: Optional[ADMMParameters] = None) -> None: + def __init__( + self, + qubo_optimizer: Optional[OptimizationAlgorithm] = None, + continuous_optimizer: Optional[OptimizationAlgorithm] = None, + params: Optional[ADMMParameters] = None, + ) -> None: """ Args: qubo_optimizer: An instance of OptimizationAlgorithm that can effectively solve @@ -228,7 +246,9 @@ def __init__(self, qubo_optimizer: Optional[OptimizationAlgorithm] = None, self._params = params or ADMMParameters() # create optimizers if not specified - self._qubo_optimizer = qubo_optimizer or MinimumEigenOptimizer(NumPyMinimumEigensolver()) + self._qubo_optimizer = qubo_optimizer or MinimumEigenOptimizer( + NumPyMinimumEigensolver() + ) self._continuous_optimizer = continuous_optimizer or SlsqpOptimizer() # internal state where we'll keep intermediate solution @@ -249,11 +269,13 @@ def get_compatibility_msg(self, problem: QuadraticProgram) -> Optional[str]: QiskitOptimizationError: If the problem is not compatible with the ADMM optimizer. """ - msg = '' + msg = "" # 1. get bin/int and continuous variable indices bin_int_indices = self._get_variable_indices(problem, Variable.Type.BINARY) - continuous_indices = self._get_variable_indices(problem, Variable.Type.CONTINUOUS) + continuous_indices = self._get_variable_indices( + problem, Variable.Type.CONTINUOUS + ) # 2. binary and continuous variables are separable in objective for bin_int_index in bin_int_indices: @@ -261,7 +283,7 @@ def get_compatibility_msg(self, problem: QuadraticProgram) -> Optional[str]: coeff = problem.objective.quadratic[bin_int_index, continuous_index] if coeff != 0: # binary and continuous vars are mixed. - msg += 'Binary and continuous variables are not separable in the objective. ' + msg += "Binary and continuous variables are not separable in the objective. " # if an error occurred, return error message, otherwise, return None return msg @@ -285,6 +307,7 @@ def solve(self, problem: QuadraticProgram) -> ADMMOptimizationResult: # map integer variables to binary variables from ..converters.integer_to_binary import IntegerToBinary + int2bin = IntegerToBinary() original_problem = problem problem = int2bin.convert(problem) @@ -296,9 +319,12 @@ def solve(self, problem: QuadraticProgram) -> ADMMOptimizationResult: self._state = ADMMState(problem, self._params.rho_initial) # parse problem and convert to an ADMM specific representation. - self._state.binary_indices = self._get_variable_indices(problem, Variable.Type.BINARY) - self._state.continuous_indices = self._get_variable_indices(problem, - Variable.Type.CONTINUOUS) + 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) @@ -311,10 +337,11 @@ def solve(self, problem: QuadraticProgram) -> ADMMOptimizationResult: # we have not stated our computations yet, so elapsed time initialized as zero. elapsed_time = 0.0 iteration = 0 - residual = 1.e+2 + residual = 1.0e2 - while (iteration < self._params.maxiter and residual > self._params.tol) \ - and (elapsed_time < self._params.max_time): + while (iteration < self._params.maxiter and residual > self._params.tol) and ( + elapsed_time < self._params.max_time + ): if self._state.step1_absolute_indices: op1 = self._create_step1_problem() self._state.x0 = self._update_x0(op1) @@ -349,8 +376,12 @@ def solve(self, problem: QuadraticProgram) -> ADMMOptimizationResult: residual, dual_residual = self._get_solution_residuals(iteration) merit = self._get_merit(cost_iterate, constraint_residual) # debug - self._log.debug("cost_iterate=%s, cr=%s, merit=%s", - cost_iterate, constraint_residual, merit) + self._log.debug( + "cost_iterate=%s, cr=%s, merit=%s", + cost_iterate, + constraint_residual, + merit, + ) # costs are saved with their original sign. self._state.cost_iterates.append(cost_iterate) @@ -376,18 +407,30 @@ def solve(self, problem: QuadraticProgram) -> ADMMOptimizationResult: # flip the objective sign again if required objective_value = objective_value * sense - self._log.debug("solution=%s, objective=%s at iteration=%s", - solution, objective_value, iteration) + self._log.debug( + "solution=%s, objective=%s at iteration=%s", + solution, + objective_value, + iteration, + ) # convert back integer to binary # `state` is our internal state of computations. - return cast(ADMMOptimizationResult, - self._interpret(x=solution, converters=int2bin, problem=original_problem, - result_class=ADMMOptimizationResult, - state=self._state)) + return cast( + ADMMOptimizationResult, + self._interpret( + x=solution, + converters=int2bin, + problem=original_problem, + result_class=ADMMOptimizationResult, + state=self._state, + ), + ) @staticmethod - def _turn_to_minimization(problem: QuadraticProgram) -> Tuple[QuadraticProgram, float]: + def _turn_to_minimization( + problem: QuadraticProgram, + ) -> Tuple[QuadraticProgram, float]: """ Turns the problem to `ObjSense.MINIMIZE` by flipping the sign of the objective function if initially it is `ObjSense.MAXIMIZE`. Otherwise returns the original problem. @@ -405,7 +448,9 @@ def _turn_to_minimization(problem: QuadraticProgram) -> Tuple[QuadraticProgram, problem.objective.sense = QuadraticObjective.Sense.MINIMIZE problem.objective.constant = (-1) * problem.objective.constant problem.objective.linear = (-1) * problem.objective.linear.coefficients - problem.objective.quadratic = (-1) * problem.objective.quadratic.coefficients + problem.objective.quadratic = ( + -1 + ) * problem.objective.quadratic.coefficients return problem, sense @staticmethod @@ -435,8 +480,9 @@ def _get_current_solution(self) -> np.ndarray: """ return self._revert_solution_indexes(self._state.x0, self._state.u) - def _revert_solution_indexes(self, binary_vars: np.ndarray, continuous_vars: np.ndarray) \ - -> np.ndarray: + def _revert_solution_indexes( + self, binary_vars: np.ndarray, continuous_vars: np.ndarray + ) -> np.ndarray: """Constructs a solution array where variables are stored in the correct order. Args: @@ -446,7 +492,9 @@ def _revert_solution_indexes(self, binary_vars: np.ndarray, continuous_vars: np. Returns: A solution array. """ - solution = np.zeros(len(self._state.binary_indices) + len(self._state.continuous_indices)) + solution = np.zeros( + len(self._state.binary_indices) + len(self._state.continuous_indices) + ) # restore solution at the original index location solution.put(self._state.binary_indices, binary_vars) solution.put(self._state.continuous_indices, continuous_vars) @@ -484,10 +532,14 @@ def _convert_problem_representation(self) -> None: # objective self._state.q0 = self._get_q(self._state.step1_absolute_indices) - c0_vec = self._state.op.objective.linear.to_array()[self._state.step1_absolute_indices] + c0_vec = self._state.op.objective.linear.to_array()[ + self._state.step1_absolute_indices + ] self._state.c0 = c0_vec self._state.q1 = self._get_q(self._state.continuous_indices) - self._state.c1 = self._state.op.objective.linear.to_array()[self._state.continuous_indices] + self._state.c1 = self._state.op.objective.linear.to_array()[ + self._state.continuous_indices + ] # equality constraints with binary vars only self._state.a0, self._state.b0 = self._get_a0_b0() @@ -507,8 +559,13 @@ def _get_step1_indices(self) -> Tuple[List[int], List[int]]: for binary_index in self._state.binary_indices: # here we check if this binary variable present in the objective # either in the linear or quadratic terms - if self._state.op.objective.linear[binary_index] != 0 or np.abs( - self._state.op.objective.quadratic.coefficients[binary_index, :]).sum() != 0: + if ( + self._state.op.objective.linear[binary_index] != 0 + or np.abs( + self._state.op.objective.quadratic.coefficients[binary_index, :] + ).sum() + != 0 + ): # add the variable if it was not added before if binary_index not in step1_absolute_indices: step1_absolute_indices.append(binary_index) @@ -521,8 +578,10 @@ def _get_step1_indices(self) -> Tuple[List[int], List[int]]: # verify if an equality contains binary variables for constraint in self._state.binary_equality_constraints: for binary_index in list(rest_binary): - if constraint.linear[binary_index] > 0 \ - and binary_index not in step1_absolute_indices: + if ( + constraint.linear[binary_index] > 0 + and binary_index not in step1_absolute_indices + ): # a binary variable with the binary_index is present in this constraint step1_absolute_indices.append(binary_index) @@ -570,8 +629,9 @@ def _get_q(self, variable_indices: List[int]) -> np.ndarray: # we build upper triangular matrix to avoid doubling of the coefficients for i in range(0, size): for j in range(i, size): - q[i, j] = \ - self._state.op.objective.quadratic[variable_indices[i], variable_indices[j]] + q[i, j] = self._state.op.objective.quadratic[ + variable_indices[i], variable_indices[j] + ] return q @@ -589,7 +649,11 @@ def _get_a0_b0(self) -> Tuple[np.ndarray, np.ndarray]: vector = [] for constraint in self._state.binary_equality_constraints: - row = constraint.linear.to_array().take(self._state.step1_absolute_indices).tolist() + row = ( + constraint.linear.to_array() + .take(self._state.step1_absolute_indices) + .tolist() + ) matrix.append(row) vector.append(constraint.rhs) @@ -598,7 +662,9 @@ def _get_a0_b0(self) -> Tuple[np.ndarray, np.ndarray]: np_matrix = np.array(matrix) np_vector = np.array(vector) else: - np_matrix = np.array([0] * len(self._state.step1_absolute_indices)).reshape((1, -1)) + np_matrix = np.array([0] * len(self._state.step1_absolute_indices)).reshape( + (1, -1) + ) np_vector = np.zeros(shape=(1,)) return np_matrix, np_vector @@ -618,17 +684,26 @@ def _create_step1_problem(self) -> QuadraticProgram: op1.binary_var(name=name) # prepare and set quadratic objective. - quadratic_objective = self._state.q0 + \ - self._params.factor_c / 2 * np.dot(self._state.a0.transpose(), self._state.a0) + \ - self._state.rho / 2 * np.eye(binary_size) + quadratic_objective = ( + self._state.q0 + + self._params.factor_c + / 2 + * np.dot(self._state.a0.transpose(), self._state.a0) + + self._state.rho / 2 * np.eye(binary_size) + ) op1.objective.quadratic = quadratic_objective # prepare and set linear objective. - linear_objective = self._state.c0 - \ - self._params.factor_c * np.dot(self._state.b0, self._state.a0) + \ - self._state.rho * (- self._state.y[self._state.step1_relative_indices] - - self._state.z[self._state.step1_relative_indices]) + \ - self._state.lambda_mult[self._state.step1_relative_indices] + linear_objective = ( + self._state.c0 + - self._params.factor_c * np.dot(self._state.b0, self._state.a0) + + self._state.rho + * ( + -self._state.y[self._state.step1_relative_indices] + - self._state.z[self._state.step1_relative_indices] + ) + + self._state.lambda_mult[self._state.step1_relative_indices] + ) op1.objective.linear = linear_objective return op1 @@ -646,13 +721,14 @@ def _create_step2_problem(self) -> QuadraticProgram: for i, var_index in enumerate(self._state.binary_indices): variable = op2.variables[var_index] variable.vartype = Variable.Type.CONTINUOUS - variable.upperbound = 1. - variable.lowerbound = 0. + variable.upperbound = 1.0 + variable.lowerbound = 0.0 # replacing Q0 objective and take of min/max sense, initially we consider minimization op2.objective.quadratic[var_index, var_index] = self._state.rho / 2 # replacing linear objective - op2.objective.linear[var_index] = -1 * self._state.lambda_mult[i] - self._state.rho * \ - (self._state.x0[i] - self._state.y[i]) + op2.objective.linear[var_index] = -1 * self._state.lambda_mult[ + i + ] - self._state.rho * (self._state.x0[i] - self._state.y[i]) # remove A0 x0 = b0 constraints for constraint in self._state.binary_equality_constraints: @@ -674,12 +750,15 @@ def _create_step3_problem(self) -> QuadraticProgram: op3.continuous_var(lowerbound=-np.inf, upperbound=np.inf, name=name) # set quadratic objective y - quadratic_y = self._params.beta / 2 * np.eye(binary_size) + \ - self._state.rho / 2 * np.eye(binary_size) + quadratic_y = self._params.beta / 2 * np.eye( + binary_size + ) + self._state.rho / 2 * np.eye(binary_size) op3.objective.quadratic = quadratic_y # set linear objective for y - linear_y = - self._state.lambda_mult - self._state.rho * (self._state.x0 - self._state.z) + linear_y = -self._state.lambda_mult - self._state.rho * ( + self._state.x0 - self._state.z + ) op3.objective.linear = linear_y return op3 @@ -753,8 +832,9 @@ def _update_lambda_mult(self) -> np.ndarray: Returns: The updated array of values of lambda multiplier. """ - return self._state.lambda_mult + \ - self._state.rho * (self._state.x0 - self._state.z - self._state.y) + return self._state.lambda_mult + self._state.rho * ( + self._state.x0 - self._state.z - self._state.y + ) def _update_rho(self, primal_residual: float, dual_residual: float) -> None: """Updating the rho parameter in ADMM. @@ -766,7 +846,7 @@ def _update_rho(self, primal_residual: float, dual_residual: float) -> None: if self._params.vary_rho == UPDATE_RHO_BY_TEN_PERCENT: # Increase rho, to aid convergence. - if self._state.rho < 1.e+10: + if self._state.rho < 1.0e10: self._state.rho *= 1.1 elif self._params.vary_rho == UPDATE_RHO_BY_RESIDUALS: if primal_residual > self._params.mu_res * dual_residual: @@ -793,7 +873,9 @@ def _get_constraint_residual(self) -> float: cr_ineq = 0.0 for constraint in self._state.inequality_constraints: sense = -1.0 if constraint.sense == Constraint.Sense.GE else 1.0 - cr_ineq += max(sense * (constraint.evaluate(solution) - constraint.rhs), 0.0) + cr_ineq += max( + sense * (constraint.evaluate(solution) - constraint.rhs), 0.0 + ) return cr_eq + cr_ineq diff --git a/qiskit_optimization/algorithms/cobyla_optimizer.py b/qiskit_optimization/algorithms/cobyla_optimizer.py index d61d9716c..a4329bd34 100644 --- a/qiskit_optimization/algorithms/cobyla_optimizer.py +++ b/qiskit_optimization/algorithms/cobyla_optimizer.py @@ -42,9 +42,16 @@ class CobylaOptimizer(MultiStartOptimizer): >>> result = optimizer.solve(problem) """ - def __init__(self, rhobeg: float = 1.0, rhoend: float = 1e-4, maxfun: int = 1000, - disp: Optional[int] = None, catol: float = 2e-4, trials: int = 1, - clip: float = 100.) -> None: + def __init__( + self, + rhobeg: float = 1.0, + rhoend: float = 1e-4, + maxfun: int = 1000, + disp: Optional[int] = None, + catol: float = 2e-4, + trials: int = 1, + clip: float = 100.0, + ) -> None: """Initializes the CobylaOptimizer. This initializer takes the algorithmic parameters of COBYLA and stores them for later use @@ -90,9 +97,9 @@ def get_compatibility_msg(self, problem: QuadraticProgram) -> str: """ # check whether there are variables of type other than continuous if len(problem.variables) > problem.get_num_continuous_vars(): - return 'The COBYLA optimizer supports only continuous variables' + return "The COBYLA optimizer supports only continuous variables" - return '' + return "" def solve(self, problem: QuadraticProgram) -> OptimizationResult: """Tries to solves the given problem using the optimizer. @@ -122,38 +129,50 @@ def objective(x): lowerbound = variable.lowerbound upperbound = variable.upperbound if lowerbound > -INFINITY: + def lb_constraint(x, l_b=lowerbound, j=i): return x[j] - l_b + constraints += [lb_constraint] if upperbound < INFINITY: + def ub_constraint(x, u_b=upperbound, j=i): return u_b - x[j] + constraints += [ub_constraint] # pylint: disable=no-member # add linear and quadratic constraints - for constraint in cast(List[Constraint], problem.linear_constraints) +\ - cast(List[Constraint], problem.quadratic_constraints): + for constraint in cast(List[Constraint], problem.linear_constraints) + cast( + List[Constraint], problem.quadratic_constraints + ): rhs = constraint.rhs sense = constraint.sense if sense == Constraint.Sense.EQ: constraints += [ lambda x, rhs=rhs, c=constraint: rhs - c.evaluate(x), - lambda x, rhs=rhs, c=constraint: c.evaluate(x) - rhs + lambda x, rhs=rhs, c=constraint: c.evaluate(x) - rhs, ] elif sense == Constraint.Sense.LE: constraints += [lambda x, rhs=rhs, c=constraint: rhs - c.evaluate(x)] elif sense == Constraint.Sense.GE: constraints += [lambda x, rhs=rhs, c=constraint: c.evaluate(x) - rhs] else: - raise QiskitOptimizationError('Unsupported constraint type!') + raise QiskitOptimizationError("Unsupported constraint type!") # actual minimization function to be called by multi_start_solve def _minimize(x_0: np.ndarray) -> Tuple[np.ndarray, Any]: - x = fmin_cobyla(objective, x_0, constraints, rhobeg=self._rhobeg, - rhoend=self._rhoend, maxfun=self._maxfun, disp=self._disp, - catol=self._catol) + x = fmin_cobyla( + objective, + x_0, + constraints, + rhobeg=self._rhobeg, + rhoend=self._rhoend, + maxfun=self._maxfun, + disp=self._disp, + catol=self._catol, + ) return x, None return self.multi_start_solve(_minimize, problem) diff --git a/qiskit_optimization/algorithms/cplex_optimizer.py b/qiskit_optimization/algorithms/cplex_optimizer.py index 68b457c73..393a3a6a1 100644 --- a/qiskit_optimization/algorithms/cplex_optimizer.py +++ b/qiskit_optimization/algorithms/cplex_optimizer.py @@ -23,6 +23,7 @@ try: from cplex.exceptions import CplexSolverError + _HAS_CPLEX = True except ImportError: _HAS_CPLEX = False @@ -54,15 +55,16 @@ def __init__(self, disp: bool = False) -> None: """ if not _HAS_CPLEX: raise MissingOptionalLibraryError( - libname='CPLEX', - name='CplexOptimizer', - pip_install="pip install 'qiskit-optimization[cplex]'") + libname="CPLEX", + name="CplexOptimizer", + pip_install="pip install 'qiskit-optimization[cplex]'", + ) self._disp = disp @staticmethod def is_cplex_installed(): - """ Returns True if cplex is installed """ + """Returns True if cplex is installed""" return _HAS_CPLEX @property @@ -96,7 +98,7 @@ def get_compatibility_msg(self, problem: QuadraticProgram) -> str: Returns: An empty string. """ - return '' + return "" def solve(self, problem: QuadraticProgram) -> OptimizationResult: """Tries to solves the given problem using the optimizer. @@ -134,10 +136,13 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: sol = cplex.solution # create results - result = OptimizationResult(x=sol.get_values(), fval=sol.get_objective_value(), - variables=problem.variables, - status=self._get_feasibility_status(problem, sol.get_values()), - raw_results=sol) + result = OptimizationResult( + x=sol.get_values(), + fval=sol.get_objective_value(), + variables=problem.variables, + status=self._get_feasibility_status(problem, sol.get_values()), + raw_results=sol, + ) # return solution return result diff --git a/qiskit_optimization/algorithms/goemans_williamson_optimizer.py b/qiskit_optimization/algorithms/goemans_williamson_optimizer.py index 42d11a5f6..a4c1a7d7a 100644 --- a/qiskit_optimization/algorithms/goemans_williamson_optimizer.py +++ b/qiskit_optimization/algorithms/goemans_williamson_optimizer.py @@ -20,14 +20,19 @@ import numpy as np from qiskit.exceptions import MissingOptionalLibraryError -from .optimization_algorithm import OptimizationResult, OptimizationResultStatus, \ - OptimizationAlgorithm, SolutionSample +from .optimization_algorithm import ( + OptimizationResult, + OptimizationResultStatus, + OptimizationAlgorithm, + SolutionSample, +) from ..problems.quadratic_program import QuadraticProgram from ..problems.variable import Variable try: import cvxpy as cvx from cvxpy import DCPError, DGPError, SolverError + _HAS_CVXPY = True except ImportError: _HAS_CVXPY = False @@ -42,10 +47,15 @@ class GoemansWilliamsonOptimizationResult(OptimizationResult): values of just one solution. Explore ``samples`` for all possible solutions. """ - def __init__(self, x: Optional[Union[List[float], np.ndarray]], fval: float, - variables: List[Variable], status: OptimizationResultStatus, - samples: Optional[List[SolutionSample]], - sdp_solution: Optional[np.ndarray] = None) -> None: + def __init__( + self, + x: Optional[Union[List[float], np.ndarray]], + fval: float, + variables: List[Variable], + status: OptimizationResultStatus, + samples: Optional[List[SolutionSample]], + sdp_solution: Optional[np.ndarray] = None, + ) -> None: """ Args: x: the optimal value found in the optimization. @@ -78,8 +88,13 @@ class GoemansWilliamsonOptimizer(OptimizationAlgorithm): the graph. """ - def __init__(self, num_cuts: int, sort_cuts: bool = True, - unique_cuts: bool = True, seed: int = 0): + def __init__( + self, + num_cuts: int, + sort_cuts: bool = True, + unique_cuts: bool = True, + seed: int = 0, + ): """ Args: num_cuts: Number of cuts to generate. @@ -95,7 +110,8 @@ def __init__(self, num_cuts: int, sort_cuts: bool = True, raise MissingOptionalLibraryError( libname="CVXPY", name="GoemansWilliamsonOptimizer", - pip_install="pip install 'qiskit-optimization[cvxpy]'") + pip_install="pip install 'qiskit-optimization[cvxpy]'", + ) super().__init__() self._num_cuts = num_cuts @@ -114,9 +130,11 @@ def get_compatibility_msg(self, problem: QuadraticProgram) -> str: """ message = "" if problem.get_num_binary_vars() != problem.get_num_vars(): - message = f"Only binary variables are supported, while the total number of variables " \ - f"{problem.get_num_vars()} and there are {problem.get_num_binary_vars()} " \ - f"binary variables across them" + message = ( + f"Only binary variables are supported, while the total number of variables " + f"{problem.get_num_vars()} and there are {problem.get_num_binary_vars()} " + f"binary variables across them" + ) return message def solve(self, problem: QuadraticProgram) -> OptimizationResult: @@ -137,15 +155,20 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: chi = self._solve_max_cut_sdp(adj_matrix) except (DCPError, DGPError, SolverError): logger.error("Can't solve SDP problem") - return GoemansWilliamsonOptimizationResult(x=[], fval=0, variables=problem.variables, - status=OptimizationResultStatus.FAILURE, - samples=[]) + return GoemansWilliamsonOptimizationResult( + x=[], + fval=0, + variables=problem.variables, + status=OptimizationResultStatus.FAILURE, + samples=[], + ) cuts = self._generate_random_cuts(chi, len(adj_matrix)) - numeric_solutions = [(cuts[i, :], - self.max_cut_value(cuts[i, :], adj_matrix)) - for i in range(self._num_cuts)] + numeric_solutions = [ + (cuts[i, :], self.max_cut_value(cuts[i, :], adj_matrix)) + for i in range(self._num_cuts) + ] if self._sort_cuts: numeric_solutions.sort(key=lambda x: -x[1]) @@ -153,22 +176,29 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: if self._unique_cuts: numeric_solutions = self._get_unique_cuts(numeric_solutions) - numeric_solutions = numeric_solutions[:self._num_cuts] - samples = [SolutionSample( - x=solution[0], - fval=solution[1], - probability=1.0 / len(numeric_solutions), - status=OptimizationResultStatus.SUCCESS) for solution in numeric_solutions] - - return GoemansWilliamsonOptimizationResult(x=samples[0].x, - fval=samples[0].fval, - variables=problem.variables, - status=OptimizationResultStatus.SUCCESS, - samples=samples, - sdp_solution=chi) - - def _get_unique_cuts(self, solutions: List[Tuple[np.ndarray, float]]) \ - -> List[Tuple[np.ndarray, float]]: + numeric_solutions = numeric_solutions[: self._num_cuts] + samples = [ + SolutionSample( + x=solution[0], + fval=solution[1], + probability=1.0 / len(numeric_solutions), + status=OptimizationResultStatus.SUCCESS, + ) + for solution in numeric_solutions + ] + + return GoemansWilliamsonOptimizationResult( + x=samples[0].x, + fval=samples[0].fval, + variables=problem.variables, + status=OptimizationResultStatus.SUCCESS, + samples=samples, + sdp_solution=chi, + ) + + def _get_unique_cuts( + self, solutions: List[Tuple[np.ndarray, float]] + ) -> List[Tuple[np.ndarray, float]]: """ Returns: Unique Goemans-Williamson cuts. @@ -179,12 +209,15 @@ def _get_unique_cuts(self, solutions: List[Tuple[np.ndarray, float]]) \ # starting from 1 to start from 0. In the next loop repetitive cuts will be removed. for idx, cut in enumerate(solutions): if cut[0][0] == 1: - solutions[idx] = (np.array([0 if _ == 1 else 1 for _ in cut[0]]), cut[1]) + solutions[idx] = ( + np.array([0 if _ == 1 else 1 for _ in cut[0]]), + cut[1], + ) seen_cuts = set() unique_cuts = [] for cut in solutions: - cut_str = ''.join([str(_) for _ in cut[0]]) + cut_str = "".join([str(_) for _ in cut[0]]) if cut_str in seen_cuts: continue @@ -230,7 +263,9 @@ def _solve_max_cut_sdp(self, adj_matrix: np.ndarray) -> np.ndarray: constraints.append(x[i, i] == 1) # objective function - expr = cvx.sum(cvx.multiply(adj_matrix, (np.ones((num_vertices, num_vertices)) - x))) + expr = cvx.sum( + cvx.multiply(adj_matrix, (np.ones((num_vertices, num_vertices)) - x)) + ) # solve problem = cvx.Problem(cvx.Maximize(expr), constraints) diff --git a/qiskit_optimization/algorithms/grover_optimizer.py b/qiskit_optimization/algorithms/grover_optimizer.py index ca14d64dd..e8dba401b 100644 --- a/qiskit_optimization/algorithms/grover_optimizer.py +++ b/qiskit_optimization/algorithms/grover_optimizer.py @@ -26,9 +26,16 @@ from qiskit.circuit.library import QuadraticForm from qiskit.providers import Backend, BaseBackend from qiskit.quantum_info import partial_trace -from .optimization_algorithm import (OptimizationResultStatus, OptimizationAlgorithm, - OptimizationResult, SolutionSample) -from ..converters.quadratic_program_to_qubo import QuadraticProgramToQubo, QuadraticProgramConverter +from .optimization_algorithm import ( + OptimizationResultStatus, + OptimizationAlgorithm, + OptimizationResult, + SolutionSample, +) +from ..converters.quadratic_program_to_qubo import ( + QuadraticProgramToQubo, + QuadraticProgramConverter, +) from ..problems import Variable from ..problems.quadratic_program import QuadraticProgram @@ -38,11 +45,16 @@ class GroverOptimizer(OptimizationAlgorithm): """Uses Grover Adaptive Search (GAS) to find the minimum of a QUBO function.""" - def __init__(self, num_value_qubits: int, num_iterations: int = 3, - quantum_instance: Optional[Union[BaseBackend, Backend, QuantumInstance]] = None, - converters: Optional[Union[QuadraticProgramConverter, - List[QuadraticProgramConverter]]] = None, - penalty: Optional[float] = None) -> None: + def __init__( + self, + num_value_qubits: int, + num_iterations: int = 3, + quantum_instance: Optional[Union[BaseBackend, Backend, QuantumInstance]] = None, + converters: Optional[ + Union[QuadraticProgramConverter, List[QuadraticProgramConverter]] + ] = None, + penalty: Optional[float] = None, + ) -> None: """ Args: num_value_qubits: The number of value qubits. @@ -79,8 +91,9 @@ def quantum_instance(self) -> QuantumInstance: return self._quantum_instance @quantum_instance.setter - def quantum_instance(self, quantum_instance: Union[Backend, - BaseBackend, QuantumInstance]) -> None: + def quantum_instance( + self, quantum_instance: Union[Backend, BaseBackend, QuantumInstance] + ) -> None: """Set the quantum instance used to run the circuits. Args: @@ -111,8 +124,9 @@ def _get_a_operator(self, qr_key_value, problem): offset = problem.objective.constant # Get circuit requirements from input. - quadratic_form = QuadraticForm(self._num_value_qubits, quadratic, linear, offset, - little_endian=False) + quadratic_form = QuadraticForm( + self._num_value_qubits, quadratic, linear, offset, little_endian=False + ) a_operator = QuantumCircuit(qr_key_value) a_operator.h(list(range(self._num_key_qubits))) @@ -122,7 +136,9 @@ def _get_a_operator(self, qr_key_value, problem): def _get_oracle(self, qr_key_value): # Build negative value oracle O. if qr_key_value is None: - qr_key_value = QuantumRegister(self._num_key_qubits + self._num_value_qubits) + qr_key_value = QuantumRegister( + self._num_key_qubits + self._num_value_qubits + ) oracle_bit = QuantumRegister(1, "oracle") oracle = QuantumCircuit(qr_key_value, oracle_bit) @@ -130,8 +146,10 @@ def _get_oracle(self, qr_key_value): def is_good_state(self, measurement): """Check whether ``measurement`` is a good state or not.""" - value = measurement[self._num_key_qubits:self._num_key_qubits + self._num_value_qubits] - return value[0] == '1' + value = measurement[ + self._num_key_qubits : self._num_key_qubits + self._num_value_qubits + ] + return value[0] == "1" return oracle, is_good_state @@ -152,7 +170,7 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: QiskitOptimizationError: If the problem is incompatible with the optimizer. """ if self.quantum_instance is None: - raise AttributeError('The quantum instance or backend has not been set.') + raise AttributeError("The quantum instance or backend has not been set.") self._verify_compatibility(problem) @@ -209,30 +227,35 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: while not improvement_found: # Determine the number of rotations. loops_with_no_improvement += 1 - rotation_count = int(np.ceil(algorithm_globals.random.uniform(0, m - 1))) + rotation_count = int( + np.ceil(algorithm_globals.random.uniform(0, m - 1)) + ) rotations += rotation_count # Apply Grover's Algorithm to find values below the threshold. # TODO: Utilize Grover's incremental feature - requires changes to Grover. - amp_problem = AmplificationProblem(oracle=oracle, - state_preparation=a_operator, - is_good_state=is_good_state) + amp_problem = AmplificationProblem( + oracle=oracle, + state_preparation=a_operator, + is_good_state=is_good_state, + ) grover = Grover() - circuit = grover.construct_circuit(problem=amp_problem, - power=rotation_count, measurement=measurement) + circuit = grover.construct_circuit( + problem=amp_problem, power=rotation_count, measurement=measurement + ) # Get the next outcome. outcome = self._measure(circuit) k = int(outcome[0:n_key], 2) - v = outcome[n_key:n_key + n_value] + v = outcome[n_key : n_key + n_value] int_v = self._bin_to_int(v, n_value) + threshold - logger.info('Outcome: %s', outcome) - logger.info('Value Q(x): %s', int_v) + logger.info("Outcome: %s", outcome) + logger.info("Value Q(x): %s", int_v) # If the value is an improvement, we update the iteration parameters (e.g. oracle). if int_v < optimum_value: optimum_key = k optimum_value = int_v - logger.info('Current Optimum Key: %s', optimum_key) - logger.info('Current Optimum Value: %s', optimum_value) + logger.info("Current Optimum Key: %s", optimum_key) + logger.info("Current Optimum Value: %s", optimum_value) improvement_found = True threshold = optimum_value @@ -242,25 +265,34 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: rho = partial_trace(self._circuit_results, indices) self._circuit_results = np.diag(rho.data) ** 0.5 else: - self._circuit_results = {i[0:n_key]: v for i, - v in self._circuit_results.items()} - - raw_samples = self._eigenvector_to_solutions(self._circuit_results, - problem_init) - raw_samples.sort(key=lambda x: problem_.objective.sense.value * x.fval) - samples = self._interpret_samples(problem, raw_samples, self._converters) + self._circuit_results = { + i[0:n_key]: v for i, v in self._circuit_results.items() + } + + raw_samples = self._eigenvector_to_solutions( + self._circuit_results, problem_init + ) + raw_samples.sort( + key=lambda x: problem_.objective.sense.value * x.fval + ) + samples = self._interpret_samples( + problem, raw_samples, self._converters + ) else: # Using Durr and Hoyer method, increase m. m = int(np.ceil(min(m * 8 / 7, 2 ** (n_key / 2)))) - logger.info('No Improvement. M: %s', m) + logger.info("No Improvement. M: %s", m) # Check if we've already seen this value. if k not in keys_measured: keys_measured.append(k) # Assume the optimal if any of the stop parameters are true. - if loops_with_no_improvement >= self._n_iterations or \ - len(keys_measured) == num_solutions or rotations >= max_rotations: + if ( + loops_with_no_improvement >= self._n_iterations + or len(keys_measured) == num_solutions + or rotations >= max_rotations + ): improvement_found = True optimum_found = True @@ -268,24 +300,35 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: operations = circuit.count_ops() operation_count[iteration] = operations iteration += 1 - logger.info('Operation Count: %s\n', operations) + logger.info("Operation Count: %s\n", operations) # If the constant is 0 and we didn't find a negative, the answer is likely 0. if optimum_value >= 0 and orig_constant == 0: optimum_key = 0 - opt_x = np.array([1 if s == '1' else 0 for s in ('{0:%sb}' % n_key).format(optimum_key)]) + opt_x = np.array( + [1 if s == "1" else 0 for s in ("{0:%sb}" % n_key).format(optimum_key)] + ) # Compute function value fval = problem_init.objective.evaluate(opt_x) # cast binaries back to integers - return cast(GroverOptimizationResult, - self._interpret(x=opt_x, converters=self._converters, problem=problem, - result_class=GroverOptimizationResult, - samples=samples, raw_samples=raw_samples, - operation_counts=operation_count, n_input_qubits=n_key, - n_output_qubits=n_value, intermediate_fval=fval, - threshold=threshold)) + return cast( + GroverOptimizationResult, + self._interpret( + x=opt_x, + converters=self._converters, + problem=problem, + result_class=GroverOptimizationResult, + samples=samples, + raw_samples=raw_samples, + operation_counts=operation_count, + n_input_qubits=n_key, + n_output_qubits=n_value, + intermediate_fval=fval, + threshold=threshold, + ), + ) def _measure(self, circuit: QuantumCircuit) -> str: """Get probabilities from the given backend, and picks a random outcome.""" @@ -293,7 +336,7 @@ def _measure(self, circuit: QuantumCircuit) -> str: freq = sorted(probs.items(), key=lambda x: x[1], reverse=True) # Pick a random outcome. idx = algorithm_globals.random.choice(len(freq), 1, p=[x[1] for x in freq])[0] - logger.info('Frequencies: %s', freq) + logger.info("Frequencies: %s", freq) return freq[idx][0] def _get_probs(self, qc: QuantumCircuit) -> Dict[str, float]: @@ -302,8 +345,10 @@ def _get_probs(self, qc: QuantumCircuit) -> Dict[str, float]: result = self.quantum_instance.execute(qc) if self.quantum_instance.is_statevector: state = result.get_statevector(qc) - keys = [bin(i)[2::].rjust(int(np.log2(len(state))), '0')[::-1] - for i in range(0, len(state))] + keys = [ + bin(i)[2::].rjust(int(np.log2(len(state))), "0")[::-1] + for i in range(0, len(state)) + ] probs = [abs(a) ** 2 for a in state] total = math.fsum(probs) probs = [p / total for p in probs] @@ -313,7 +358,9 @@ def _get_probs(self, qc: QuantumCircuit) -> Dict[str, float]: state = result.get_counts(qc) shots = self.quantum_instance.run_config.shots hist = {key[::-1]: val / shots for key, val in state.items() if val > 0} - self._circuit_results = {b[::-1]: np.sqrt(v / shots) for (b, v) in state.items()} + self._circuit_results = { + b[::-1]: np.sqrt(v / shots) for (b, v) in state.items() + } return hist @staticmethod @@ -330,11 +377,20 @@ def _bin_to_int(v: str, num_value_bits: int) -> int: class GroverOptimizationResult(OptimizationResult): """A result object for Grover Optimization methods.""" - def __init__(self, x: Union[List[float], np.ndarray], fval: float, variables: List[Variable], - operation_counts: Dict[int, Dict[str, int]], n_input_qubits: int, - n_output_qubits: int, intermediate_fval: float, threshold: float, - status: OptimizationResultStatus, samples: Optional[List[SolutionSample]] = None, - raw_samples: Optional[List[SolutionSample]] = None) -> None: + def __init__( + self, + x: Union[List[float], np.ndarray], + fval: float, + variables: List[Variable], + operation_counts: Dict[int, Dict[str, int]], + n_input_qubits: int, + n_output_qubits: int, + intermediate_fval: float, + threshold: float, + status: OptimizationResultStatus, + samples: Optional[List[SolutionSample]] = None, + raw_samples: Optional[List[SolutionSample]] = None, + ) -> None: """ Constructs a result object with the specific Grover properties. @@ -354,8 +410,14 @@ def __init__(self, x: Union[List[float], np.ndarray], fval: float, variables: Li raw_samples: the x values of the QUBO, the objective function value of the QUBO, and the probability of sampling. """ - super().__init__(x=x, fval=fval, variables=variables, status=status, raw_results=None, - samples=samples) + super().__init__( + x=x, + fval=fval, + variables=variables, + status=status, + raw_results=None, + samples=samples, + ) self._raw_samples = raw_samples self._operation_counts = operation_counts self._n_input_qubits = n_input_qubits diff --git a/qiskit_optimization/algorithms/minimum_eigen_optimizer.py b/qiskit_optimization/algorithms/minimum_eigen_optimizer.py index 3ebb43c81..d4b60eadb 100644 --- a/qiskit_optimization/algorithms/minimum_eigen_optimizer.py +++ b/qiskit_optimization/algorithms/minimum_eigen_optimizer.py @@ -17,21 +17,33 @@ from qiskit.algorithms import MinimumEigensolver, MinimumEigensolverResult from qiskit.opflow import OperatorBase -from .optimization_algorithm import (OptimizationResultStatus, OptimizationAlgorithm, - OptimizationResult, SolutionSample) +from .optimization_algorithm import ( + OptimizationResultStatus, + OptimizationAlgorithm, + OptimizationResult, + SolutionSample, +) from ..exceptions import QiskitOptimizationError -from ..converters.quadratic_program_to_qubo import QuadraticProgramToQubo, QuadraticProgramConverter +from ..converters.quadratic_program_to_qubo import ( + QuadraticProgramToQubo, + QuadraticProgramConverter, +) from ..problems.quadratic_program import QuadraticProgram, Variable class MinimumEigenOptimizationResult(OptimizationResult): - """ Minimum Eigen Optimizer Result.""" - - def __init__(self, x: Union[List[float], np.ndarray], fval: float, variables: List[Variable], - status: OptimizationResultStatus, - samples: Optional[List[SolutionSample]] = None, - min_eigen_solver_result: Optional[MinimumEigensolverResult] = None, - raw_samples: Optional[List[SolutionSample]] = None) -> None: + """Minimum Eigen Optimizer Result.""" + + def __init__( + self, + x: Union[List[float], np.ndarray], + fval: float, + variables: List[Variable], + status: OptimizationResultStatus, + samples: Optional[List[SolutionSample]] = None, + min_eigen_solver_result: Optional[MinimumEigensolverResult] = None, + raw_samples: Optional[List[SolutionSample]] = None, + ) -> None: """ Args: x: the optimal value found by ``MinimumEigensolver``. @@ -44,8 +56,14 @@ def __init__(self, x: Union[List[float], np.ndarray], fval: float, variables: Li raw_samples: the x values of the QUBO, the objective function value of the QUBO, and the probability of sampling. """ - super().__init__(x=x, fval=fval, variables=variables, status=status, raw_results=None, - samples=samples) + super().__init__( + x=x, + fval=fval, + variables=variables, + status=status, + raw_results=None, + samples=samples, + ) self._min_eigen_solver_result = min_eigen_solver_result self._raw_samples = raw_samples @@ -94,9 +112,14 @@ class MinimumEigenOptimizer(OptimizationAlgorithm): result = optimizer.solve(problem) """ - def __init__(self, min_eigen_solver: MinimumEigensolver, penalty: Optional[float] = None, - converters: Optional[Union[QuadraticProgramConverter, - List[QuadraticProgramConverter]]] = None) -> None: + def __init__( + self, + min_eigen_solver: MinimumEigensolver, + penalty: Optional[float] = None, + converters: Optional[ + Union[QuadraticProgramConverter, List[QuadraticProgramConverter]] + ] = None, + ) -> None: """ This initializer takes the minimum eigen solver to be used to approximate the ground state of the resulting Hamiltonian as well as a optional penalty factor to scale penalty terms @@ -116,8 +139,10 @@ def __init__(self, min_eigen_solver: MinimumEigensolver, penalty: Optional[float """ if not min_eigen_solver.supports_aux_operators(): - raise QiskitOptimizationError('Given MinimumEigensolver does not return the eigenstate ' - + 'and is not supported by the MinimumEigenOptimizer.') + raise QiskitOptimizationError( + "Given MinimumEigensolver does not return the eigenstate " + + "and is not supported by the MinimumEigenOptimizer." + ) self._min_eigen_solver = min_eigen_solver self._penalty = penalty @@ -171,11 +196,13 @@ def solve(self, problem: QuadraticProgram) -> MinimumEigenOptimizationResult: return self._solve_internal(operator, offset, problem_, problem) - def _solve_internal(self, - operator: OperatorBase, - offset: float, - converted_problem: QuadraticProgram, - original_problem: QuadraticProgram): + def _solve_internal( + self, + operator: OperatorBase, + offset: float, + converted_problem: QuadraticProgram, + original_problem: QuadraticProgram, + ): # only try to solve non-empty Ising Hamiltonians x = None # type: Optional[Any] @@ -190,8 +217,11 @@ def _solve_internal(self, raw_samples = None if eigen_result.eigenstate is not None: raw_samples = self._eigenvector_to_solutions( - eigen_result.eigenstate, converted_problem) - raw_samples.sort(key=lambda x: converted_problem.objective.sense.value * x.fval) + eigen_result.eigenstate, converted_problem + ) + raw_samples.sort( + key=lambda x: converted_problem.objective.sense.value * x.fval + ) x = raw_samples[0].x fval = raw_samples[0].fval @@ -199,20 +229,35 @@ def _solve_internal(self, else: x = np.zeros(converted_problem.get_num_binary_vars()) fval = offset - raw_samples = [SolutionSample(x, fval, 1.0, OptimizationResultStatus.SUCCESS)] + raw_samples = [ + SolutionSample(x, fval, 1.0, OptimizationResultStatus.SUCCESS) + ] if fval is None or x is None: # if not function value is given, then something went wrong, e.g., a # NumPyMinimumEigensolver has been configured with an infeasible filter criterion. - return MinimumEigenOptimizationResult(x=None, fval=None, - variables=original_problem.variables, - status=OptimizationResultStatus.FAILURE, - samples=None, raw_samples=None, - min_eigen_solver_result=eigen_result) + return MinimumEigenOptimizationResult( + x=None, + fval=None, + variables=original_problem.variables, + status=OptimizationResultStatus.FAILURE, + samples=None, + raw_samples=None, + min_eigen_solver_result=eigen_result, + ) # translate result back to integers - samples = self._interpret_samples(original_problem, raw_samples, self._converters) - return cast(MinimumEigenOptimizationResult, - self._interpret(x=x, converters=self._converters, problem=original_problem, - result_class=MinimumEigenOptimizationResult, - samples=samples, raw_samples=raw_samples, - min_eigen_solver_result=eigen_result)) + samples = self._interpret_samples( + original_problem, raw_samples, self._converters + ) + return cast( + MinimumEigenOptimizationResult, + self._interpret( + x=x, + converters=self._converters, + problem=original_problem, + result_class=MinimumEigenOptimizationResult, + samples=samples, + raw_samples=raw_samples, + min_eigen_solver_result=eigen_result, + ), + ) diff --git a/qiskit_optimization/algorithms/multistart_optimizer.py b/qiskit_optimization/algorithms/multistart_optimizer.py index b952cb6e5..965683ae3 100644 --- a/qiskit_optimization/algorithms/multistart_optimizer.py +++ b/qiskit_optimization/algorithms/multistart_optimizer.py @@ -38,7 +38,7 @@ class MultiStartOptimizer(OptimizationAlgorithm, ABC): other optimizers. """ - def __init__(self, trials: int = 1, clip: float = 100.) -> None: + def __init__(self, trials: int = 1, clip: float = 100.0) -> None: """ Constructs an instance of this optimizer. @@ -55,8 +55,11 @@ def __init__(self, trials: int = 1, clip: float = 100.) -> None: self._trials = trials self._clip = clip - def multi_start_solve(self, minimize: Callable[[np.ndarray], Tuple[np.ndarray, Any]], - problem: QuadraticProgram) -> OptimizationResult: + def multi_start_solve( + self, + minimize: Callable[[np.ndarray], Tuple[np.ndarray, Any]], + problem: QuadraticProgram, + ) -> OptimizationResult: """Applies a multi start method given a local optimizer. Args: @@ -75,8 +78,12 @@ def multi_start_solve(self, minimize: Callable[[np.ndarray], Tuple[np.ndarray, A x_0 = np.zeros(problem.get_num_vars()) if trial > 0: for i, var in enumerate(problem.variables): - lowerbound = var.lowerbound if var.lowerbound > -INFINITY else -self._clip - upperbound = var.upperbound if var.upperbound < INFINITY else self._clip + lowerbound = ( + var.lowerbound if var.lowerbound > -INFINITY else -self._clip + ) + upperbound = ( + var.upperbound if var.upperbound < INFINITY else self._clip + ) x_0[i] = uniform.rvs(lowerbound, (upperbound - lowerbound)) # run optimization t_0 = time.time() @@ -92,13 +99,17 @@ def multi_start_solve(self, minimize: Callable[[np.ndarray], Tuple[np.ndarray, A x_sol = x rest_sol = rest - return OptimizationResult(x=x_sol, fval=fval_sol, variables=problem.variables, - status=self._get_feasibility_status(problem, x_sol), - raw_results=rest_sol) + return OptimizationResult( + x=x_sol, + fval=fval_sol, + variables=problem.variables, + status=self._get_feasibility_status(problem, x_sol), + raw_results=rest_sol, + ) @property def trials(self) -> int: - """ Returns the number of trials for this optimizer. + """Returns the number of trials for this optimizer. Returns: The number of trials. @@ -116,7 +127,7 @@ def trials(self, trials: int) -> None: @property def clip(self) -> float: - """ Returns the clip value for this optimizer. + """Returns the clip value for this optimizer. Returns: The clip value. diff --git a/qiskit_optimization/algorithms/optimization_algorithm.py b/qiskit_optimization/algorithms/optimization_algorithm.py index 88db2a5af..b302fa2f8 100644 --- a/qiskit_optimization/algorithms/optimization_algorithm.py +++ b/qiskit_optimization/algorithms/optimization_algorithm.py @@ -22,8 +22,10 @@ from qiskit.opflow import StateFn, DictStateFn from ..exceptions import QiskitOptimizationError -from ..converters.quadratic_program_to_qubo import (QuadraticProgramToQubo, - QuadraticProgramConverter) +from ..converters.quadratic_program_to_qubo import ( + QuadraticProgramToQubo, + QuadraticProgramConverter, +) from ..problems.quadratic_program import QuadraticProgram, Variable @@ -50,6 +52,7 @@ class SolutionSample: probability: the probability of this sample status: the status of this sample """ + x: np.ndarray fval: float probability: float @@ -96,11 +99,15 @@ class OptimizationResult: should maintain the order when generating a new ``OptimizationResult`` object. """ - def __init__(self, x: Optional[Union[List[float], np.ndarray]], fval: float, - variables: List[Variable], - status: OptimizationResultStatus, - raw_results: Optional[Any] = None, - samples: Optional[List[SolutionSample]] = None) -> None: + def __init__( + self, + x: Optional[Union[List[float], np.ndarray]], + fval: float, + variables: List[Variable], + status: OptimizationResultStatus, + raw_results: Optional[Any] = None, + samples: Optional[List[SolutionSample]] = None, + ) -> None: """ Args: x: the optimal value found in the optimization, or possibly None in case of FAILURE. @@ -122,9 +129,11 @@ def __init__(self, x: Optional[Union[List[float], np.ndarray]], fval: float, else: if len(x) != len(variables): raise QiskitOptimizationError( - 'Inconsistent size of optimal value and variables. x: size {} {}, ' - 'variables: size {} {}'.format(len(x), x, len(variables), - [v.name for v in variables])) + "Inconsistent size of optimal value and variables. x: size {} {}, " + "variables: size {} {}".format( + len(x), x, len(variables), [v.name for v in variables] + ) + ) self._x = np.asarray(x) self._variables_dict = dict(zip(self._variable_names, self._x)) @@ -134,16 +143,25 @@ def __init__(self, x: Optional[Union[List[float], np.ndarray]], fval: float, if samples: sum_prob = np.sum([e.probability for e in samples]) if not np.isclose(sum_prob, 1.0): - warn('The sum of probability of samples is not close to 1: {}'.format(sum_prob)) + warn( + "The sum of probability of samples is not close to 1: {}".format( + sum_prob + ) + ) self._samples = samples else: self._samples = [ - SolutionSample(x=cast(np.ndarray, x), fval=fval, status=status, probability=1.0)] + SolutionSample( + x=cast(np.ndarray, x), fval=fval, status=status, probability=1.0 + ) + ] def __repr__(self) -> str: - return 'optimal function value: {}\n' \ - 'optimal value: {}\n' \ - 'status: {}'.format(self._fval, self._x, self._status.name) + return ( + "optimal function value: {}\n" + "optimal value: {}\n" + "status: {}".format(self._fval, self._x, self._status.name) + ) def __getitem__(self, key: Union[int, str]) -> float: """Returns the value of the variable whose index or name is equal to ``key``. @@ -171,7 +189,8 @@ def __getitem__(self, key: Union[int, str]) -> float: return self._variables_dict[key] raise TypeError( "Integer or string key required," - "instead {}({}) provided.".format(type(key), key)) + "instead {}({}) provided.".format(type(key), key) + ) def get_correlations(self) -> np.ndarray: """ @@ -297,7 +316,7 @@ def is_compatible(self, problem: QuadraticProgram) -> bool: return len(self.get_compatibility_msg(problem)) == 0 @abstractmethod - def solve(self, problem: QuadraticProgram) -> 'OptimizationResult': + def solve(self, problem: QuadraticProgram) -> "OptimizationResult": """Tries to solves the given problem using the optimizer. Runs the optimizer to try to solve the optimization problem. @@ -331,11 +350,12 @@ def _verify_compatibility(self, problem: QuadraticProgram) -> None: # check compatibility and raise exception if incompatible msg = self.get_compatibility_msg(problem) if msg: - raise QiskitOptimizationError('Incompatible problem: {}'.format(msg)) + raise QiskitOptimizationError("Incompatible problem: {}".format(msg)) @staticmethod - def _get_feasibility_status(problem: QuadraticProgram, - x: Union[List[float], np.ndarray]) -> OptimizationResultStatus: + def _get_feasibility_status( + problem: QuadraticProgram, x: Union[List[float], np.ndarray] + ) -> OptimizationResultStatus: """Returns whether the input result is feasible or not for the given problem. Args: @@ -347,13 +367,19 @@ def _get_feasibility_status(problem: QuadraticProgram, """ is_feasible = problem.is_feasible(x) - return OptimizationResultStatus.SUCCESS if is_feasible \ + return ( + OptimizationResultStatus.SUCCESS + if is_feasible else OptimizationResultStatus.INFEASIBLE + ) @staticmethod - def _prepare_converters(converters: Optional[Union[QuadraticProgramConverter, - List[QuadraticProgramConverter]]], - penalty: Optional[float] = None) -> List[QuadraticProgramConverter]: + def _prepare_converters( + converters: Optional[ + Union[QuadraticProgramConverter, List[QuadraticProgramConverter]] + ], + penalty: Optional[float] = None, + ) -> List[QuadraticProgramConverter]: """Prepare a list of converters from the input. Args: @@ -374,16 +400,20 @@ def _prepare_converters(converters: Optional[Union[QuadraticProgramConverter, return [QuadraticProgramToQubo(penalty=penalty)] elif isinstance(converters, QuadraticProgramConverter): return [converters] - elif isinstance(converters, list) and \ - all(isinstance(converter, QuadraticProgramConverter) for converter in converters): + elif isinstance(converters, list) and all( + isinstance(converter, QuadraticProgramConverter) for converter in converters + ): return converters else: - raise TypeError('`converters` must all be of the QuadraticProgramConverter type') + raise TypeError( + "`converters` must all be of the QuadraticProgramConverter type" + ) @staticmethod - def _convert(problem: QuadraticProgram, - converters: Union[QuadraticProgramConverter, - List[QuadraticProgramConverter]]) -> QuadraticProgram: + def _convert( + problem: QuadraticProgram, + converters: Union[QuadraticProgramConverter, List[QuadraticProgramConverter]], + ) -> QuadraticProgram: """Convert the problem with the converters Args: @@ -404,12 +434,16 @@ def _convert(problem: QuadraticProgram, return problem_ @classmethod - def _interpret(cls, x: np.ndarray, - problem: QuadraticProgram, - converters: Optional[Union[QuadraticProgramConverter, - List[QuadraticProgramConverter]]] = None, - result_class: Type[OptimizationResult] = OptimizationResult, - **kwargs) -> OptimizationResult: + def _interpret( + cls, + x: np.ndarray, + problem: QuadraticProgram, + converters: Optional[ + Union[QuadraticProgramConverter, List[QuadraticProgramConverter]] + ] = None, + result_class: Type[OptimizationResult] = OptimizationResult, + **kwargs + ) -> OptimizationResult: """Convert back the result of the converted problem to the result of the original problem. Args: @@ -430,25 +464,33 @@ def _interpret(cls, x: np.ndarray, """ if not issubclass(result_class, OptimizationResult): raise QiskitOptimizationError( - 'Invalid result class, not derived from OptimizationResult: ' - '{}'.format(result_class)) + "Invalid result class, not derived from OptimizationResult: " + "{}".format(result_class) + ) if converters is None: converters = [] if not isinstance(converters, list): converters = [converters] if not all(isinstance(conv, QuadraticProgramConverter) for conv in converters): - raise TypeError('Invalid object of converters: {}'.format(converters)) + raise TypeError("Invalid object of converters: {}".format(converters)) for converter in converters[::-1]: x = converter.interpret(x) - return result_class(x=x, fval=problem.objective.evaluate(x), - variables=problem.variables, - status=cls._get_feasibility_status(problem, x), - **kwargs) + return result_class( + x=x, + fval=problem.objective.evaluate(x), + variables=problem.variables, + status=cls._get_feasibility_status(problem, x), + **kwargs + ) @classmethod - def _interpret_samples(cls, problem: QuadraticProgram, raw_samples: List[SolutionSample], - converters: List[QuadraticProgramConverter]) -> List[SolutionSample]: + def _interpret_samples( + cls, + problem: QuadraticProgram, + raw_samples: List[SolutionSample], + converters: List[QuadraticProgramConverter], + ) -> List[SolutionSample]: prob = {} # type: dict array = {} for sample in raw_samples: @@ -466,14 +508,17 @@ def _interpret_samples(cls, problem: QuadraticProgram, raw_samples: List[Solutio status = cls._get_feasibility_status(problem, x) samples.append(SolutionSample(x, fval, probability, status)) - return sorted(samples, - key=lambda v: (v.status.value, problem.objective.sense.value * v.fval)) + return sorted( + samples, + key=lambda v: (v.status.value, problem.objective.sense.value * v.fval), + ) @staticmethod - def _eigenvector_to_solutions(eigenvector: Union[dict, np.ndarray, StateFn], - qubo: QuadraticProgram, - min_probability: float = 1e-6, - ) -> List[SolutionSample]: + def _eigenvector_to_solutions( + eigenvector: Union[dict, np.ndarray, StateFn], + qubo: QuadraticProgram, + min_probability: float = 1e-6, + ) -> List[SolutionSample]: """Convert the eigenvector to the bitstrings and corresponding eigenvalues. Args: @@ -503,15 +548,21 @@ def _eigenvector_to_solutions(eigenvector: Union[dict, np.ndarray, StateFn], TypeError: If the type of eigenvector is not supported. """ if isinstance(eigenvector, DictStateFn): - eigenvector = {bitstr: val ** 2 for (bitstr, val) in eigenvector.primitive.items()} + eigenvector = { + bitstr: val ** 2 for (bitstr, val) in eigenvector.primitive.items() + } elif isinstance(eigenvector, StateFn): eigenvector = eigenvector.to_matrix() def generate_solution(bitstr, qubo, probability): x = np.fromiter(list(bitstr), dtype=int) fval = qubo.objective.evaluate(x) - return SolutionSample(x=x, fval=fval, probability=probability, - status=OptimizationResultStatus.SUCCESS) + return SolutionSample( + x=x, + fval=fval, + probability=probability, + status=OptimizationResultStatus.SUCCESS, + ) solutions = [] if isinstance(eigenvector, dict): @@ -521,7 +572,9 @@ def generate_solution(bitstr, qubo, probability): sampling_probability = count / all_counts # add the bitstring, if the sampling probability exceeds the threshold if sampling_probability >= min_probability: - solutions.append(generate_solution(bitstr, qubo, sampling_probability)) + solutions.append( + generate_solution(bitstr, qubo, sampling_probability) + ) elif isinstance(eigenvector, np.ndarray): num_qubits = int(np.log2(eigenvector.size)) @@ -531,10 +584,14 @@ def generate_solution(bitstr, qubo, probability): for i, sampling_probability in enumerate(probabilities): # add the i-th state if the sampling probability exceeds the threshold if sampling_probability >= min_probability: - bitstr = '{:b}'.format(i).rjust(num_qubits, '0')[::-1] - solutions.append(generate_solution(bitstr, qubo, sampling_probability)) + bitstr = "{:b}".format(i).rjust(num_qubits, "0")[::-1] + solutions.append( + generate_solution(bitstr, qubo, sampling_probability) + ) else: - raise TypeError('Unsupported format of eigenvector. Provide a dict or numpy.ndarray.') + raise TypeError( + "Unsupported format of eigenvector. Provide a dict or numpy.ndarray." + ) return solutions diff --git a/qiskit_optimization/algorithms/recursive_minimum_eigen_optimizer.py b/qiskit_optimization/algorithms/recursive_minimum_eigen_optimizer.py index 0065287e0..ca6899f1b 100644 --- a/qiskit_optimization/algorithms/recursive_minimum_eigen_optimizer.py +++ b/qiskit_optimization/algorithms/recursive_minimum_eigen_optimizer.py @@ -21,10 +21,19 @@ from qiskit.algorithms import NumPyMinimumEigensolver from qiskit.utils.validation import validate_min -from .minimum_eigen_optimizer import MinimumEigenOptimizer, MinimumEigenOptimizationResult -from .optimization_algorithm import (OptimizationResultStatus, OptimizationAlgorithm, - OptimizationResult) -from ..converters.quadratic_program_to_qubo import QuadraticProgramToQubo, QuadraticProgramConverter +from .minimum_eigen_optimizer import ( + MinimumEigenOptimizer, + MinimumEigenOptimizationResult, +) +from .optimization_algorithm import ( + OptimizationResultStatus, + OptimizationAlgorithm, + OptimizationResult, +) +from ..converters.quadratic_program_to_qubo import ( + QuadraticProgramToQubo, + QuadraticProgramConverter, +) from ..exceptions import QiskitOptimizationError from ..problems import Variable from ..problems.quadratic_program import QuadraticProgram @@ -52,9 +61,15 @@ class IntermediateResult(Enum): class RecursiveMinimumEigenOptimizationResult(OptimizationResult): """Recursive Eigen Optimizer Result.""" - def __init__(self, x: Union[List[float], np.ndarray], fval: float, variables: List[Variable], - status: OptimizationResultStatus, replacements: Dict[str, Tuple[str, int]], - history: Tuple[List[MinimumEigenOptimizationResult], OptimizationResult]) -> None: + def __init__( + self, + x: Union[List[float], np.ndarray], + fval: float, + variables: List[Variable], + status: OptimizationResultStatus, + replacements: Dict[str, Tuple[str, int]], + history: Tuple[List[MinimumEigenOptimizationResult], OptimizationResult], + ) -> None: """ Constructs an instance of the result class. @@ -84,7 +99,9 @@ def replacements(self) -> Dict[str, Tuple[str, int]]: return self._replacements @property - def history(self) -> Tuple[List[MinimumEigenOptimizationResult], OptimizationResult]: + def history( + self, + ) -> Tuple[List[MinimumEigenOptimizationResult], OptimizationResult]: """ Returns intermediate results. The first element is a list of :class:`~qiskit_optimization.algorithms.MinimumEigenOptimizerResult` obtained by invoking @@ -124,13 +141,18 @@ class RecursiveMinimumEigenOptimizer(OptimizationAlgorithm): from Symmetry Protection. http://arxiv.org/abs/1910.08980. """ - def __init__(self, optimizer: OptimizationAlgorithm, min_num_vars: int = 1, - min_num_vars_optimizer: Optional[OptimizationAlgorithm] = None, - penalty: Optional[float] = None, - history: Optional[IntermediateResult] = IntermediateResult.LAST_ITERATION, - converters: Optional[Union[QuadraticProgramConverter, - List[QuadraticProgramConverter]]] = None) -> None: - """ Initializes the recursive minimum eigen optimizer. + def __init__( + self, + optimizer: OptimizationAlgorithm, + min_num_vars: int = 1, + min_num_vars_optimizer: Optional[OptimizationAlgorithm] = None, + penalty: Optional[float] = None, + history: Optional[IntermediateResult] = IntermediateResult.LAST_ITERATION, + converters: Optional[ + Union[QuadraticProgramConverter, List[QuadraticProgramConverter]] + ] = None, + ) -> None: + """Initializes the recursive minimum eigen optimizer. This initializer takes an ``OptimizationAlgorithm``, the parameters to specify until when to to apply the iterative scheme, and the optimizer to be applied once the threshold number of @@ -157,14 +179,16 @@ def __init__(self, optimizer: OptimizationAlgorithm, min_num_vars: int = 1, TypeError: When there one of converters is an invalid type. """ - validate_min('min_num_vars', min_num_vars, 1) + validate_min("min_num_vars", min_num_vars, 1) self._optimizer = optimizer self._min_num_vars = min_num_vars if min_num_vars_optimizer: self._min_num_vars_optimizer = min_num_vars_optimizer else: - self._min_num_vars_optimizer = MinimumEigenOptimizer(NumPyMinimumEigensolver()) + self._min_num_vars_optimizer = MinimumEigenOptimizer( + NumPyMinimumEigensolver() + ) self._penalty = penalty self._history = history @@ -225,7 +249,9 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: # set x_i = x_j problem_ = problem_.substitute_variables(variables={i: (j, 1)}) if problem_.status == QuadraticProgram.Status.INFEASIBLE: - raise QiskitOptimizationError('Infeasible due to variable substitution') + raise QiskitOptimizationError( + "Infeasible due to variable substitution" + ) replacements[x_i] = (x_j, 1) else: # set x_i = 1 - x_j, this is done in two steps: @@ -255,7 +281,9 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: # 2. replace x_i by -x_j problem_ = problem_.substitute_variables(variables={i: (j, -1)}) if problem_.status == QuadraticProgram.Status.INFEASIBLE: - raise QiskitOptimizationError('Infeasible due to variable substitution') + raise QiskitOptimizationError( + "Infeasible due to variable substitution" + ) replacements[x_i] = (x_j, -1) # solve remaining problem @@ -279,7 +307,7 @@ def find_value(x, replacements, var_values): var_values[x] = value if sgn == 1 else 1 - value return var_values[x] else: - raise QiskitOptimizationError('Invalid values!') + raise QiskitOptimizationError("Invalid values!") # loop over all variables to set their values for x_i in problem_ref.variables: @@ -288,15 +316,24 @@ def find_value(x, replacements, var_values): # build history before any translations are applied # min_eigen_results is an empty list if history is set to NO or LAST. - history = (optimization_results, - None if self._history == IntermediateResult.NO_ITERATIONS else result) + history = ( + optimization_results, + None if self._history == IntermediateResult.NO_ITERATIONS else result, + ) # construct result x_v = np.array([var_values[x_aux.name] for x_aux in problem_ref.variables]) - return cast(RecursiveMinimumEigenOptimizationResult, - self._interpret(x=x_v, converters=self._converters, problem=problem, - result_class=RecursiveMinimumEigenOptimizationResult, - replacements=replacements, history=history)) + return cast( + RecursiveMinimumEigenOptimizationResult, + self._interpret( + x=x_v, + converters=self._converters, + problem=problem, + result_class=RecursiveMinimumEigenOptimizationResult, + replacements=replacements, + history=history, + ), + ) @staticmethod def _find_strongest_correlation(correlations): diff --git a/qiskit_optimization/algorithms/slsqp_optimizer.py b/qiskit_optimization/algorithms/slsqp_optimizer.py index d0653dfed..c1c1a5b74 100644 --- a/qiskit_optimization/algorithms/slsqp_optimizer.py +++ b/qiskit_optimization/algorithms/slsqp_optimizer.py @@ -31,10 +31,18 @@ class SlsqpOptimizationResult(OptimizationResult): """ SLSQP optimization result, defines additional properties that may be returned by the optimizer. """ - def __init__(self, x: Union[List[float], np.ndarray], fval: float, variables: List[Variable], - status: OptimizationResultStatus, fx: Optional[np.ndarray] = None, - its: Optional[int] = None, imode: Optional[int] = None, - smode: Optional[str] = None) -> None: + + def __init__( + self, + x: Union[List[float], np.ndarray], + fval: float, + variables: List[Variable], + status: OptimizationResultStatus, + fx: Optional[np.ndarray] = None, + its: Optional[int] = None, + imode: Optional[int] = None, + smode: Optional[str] = None, + ) -> None: """ Constructs a result object with properties specific to SLSQP. @@ -98,8 +106,15 @@ class SlsqpOptimizer(MultiStartOptimizer): """ # pylint: disable=redefined-builtin - def __init__(self, iter: int = 100, acc: float = 1.0E-6, iprint: int = 0, trials: int = 1, - clip: float = 100., full_output: bool = False) -> None: + def __init__( + self, + iter: int = 100, + acc: float = 1.0e-6, + iprint: int = 0, + trials: int = 1, + clip: float = 100.0, + full_output: bool = False, + ) -> None: """Initializes the SlsqpOptimizer. This initializer takes the algorithmic parameters of SLSQP and stores them for later use @@ -149,9 +164,9 @@ def get_compatibility_msg(self, problem: QuadraticProgram) -> str: """ # check whether there are variables of type other than continuous if len(problem.variables) > problem.get_num_continuous_vars(): - return 'The SLSQP optimizer supports only continuous variables' + return "The SLSQP optimizer supports only continuous variables" - return '' + return "" def solve(self, problem: QuadraticProgram) -> OptimizationResult: """Tries to solves the given problem using the optimizer. @@ -174,7 +189,9 @@ def _objective(x): return problem.objective.sense.value * problem.objective.evaluate(x) def _objective_gradient(x): - return problem.objective.sense.value * problem.objective.evaluate_gradient(x) + return problem.objective.sense.value * problem.objective.evaluate_gradient( + x + ) # initialize constraints and bounds slsqp_bounds = [] @@ -189,26 +206,41 @@ def _objective_gradient(x): # pylint: disable=no-member # add linear and quadratic constraints - for constraint in cast(List[Constraint], problem.linear_constraints) + \ - cast(List[Constraint], problem.quadratic_constraints): + for constraint in cast(List[Constraint], problem.linear_constraints) + cast( + List[Constraint], problem.quadratic_constraints + ): rhs = constraint.rhs sense = constraint.sense if sense == Constraint.Sense.EQ: - slsqp_eq_constraints += [lambda x, rhs=rhs, c=constraint: rhs - c.evaluate(x)] + slsqp_eq_constraints += [ + lambda x, rhs=rhs, c=constraint: rhs - c.evaluate(x) + ] elif sense == Constraint.Sense.LE: - slsqp_ineq_constraints += [lambda x, rhs=rhs, c=constraint: rhs - c.evaluate(x)] + slsqp_ineq_constraints += [ + lambda x, rhs=rhs, c=constraint: rhs - c.evaluate(x) + ] elif sense == Constraint.Sense.GE: - slsqp_ineq_constraints += [lambda x, rhs=rhs, c=constraint: c.evaluate(x) - rhs] + slsqp_ineq_constraints += [ + lambda x, rhs=rhs, c=constraint: c.evaluate(x) - rhs + ] else: - raise QiskitOptimizationError('Unsupported constraint type!') + raise QiskitOptimizationError("Unsupported constraint type!") # actual minimization function to be called by multi_start_solve def _minimize(x_0: np.ndarray) -> Tuple[np.ndarray, Any]: - output = fmin_slsqp(_objective, x_0, eqcons=slsqp_eq_constraints, - ieqcons=slsqp_ineq_constraints, bounds=slsqp_bounds, - fprime=_objective_gradient, iter=self._iter, acc=self._acc, - iprint=self._iprint, full_output=self._full_output) + output = fmin_slsqp( + _objective, + x_0, + eqcons=slsqp_eq_constraints, + ieqcons=slsqp_ineq_constraints, + bounds=slsqp_bounds, + fprime=_objective_gradient, + iter=self._iter, + acc=self._acc, + iprint=self._iprint, + full_output=self._full_output, + ) if self._full_output: x, *rest = output else: @@ -219,10 +251,20 @@ def _minimize(x_0: np.ndarray) -> Tuple[np.ndarray, Any]: result = self.multi_start_solve(_minimize, problem) if self._full_output: - return SlsqpOptimizationResult(x=result.x, fval=result.fval, variables=result.variables, - status=self._get_feasibility_status(problem, result.x), - fx=result.raw_results[0], its=result.raw_results[1], - imode=result.raw_results[2], smode=result.raw_results[3]) + return SlsqpOptimizationResult( + x=result.x, + fval=result.fval, + variables=result.variables, + status=self._get_feasibility_status(problem, result.x), + fx=result.raw_results[0], + its=result.raw_results[1], + imode=result.raw_results[2], + smode=result.raw_results[3], + ) else: - return SlsqpOptimizationResult(x=result.x, fval=result.fval, variables=result.variables, - status=self._get_feasibility_status(problem, result.x)) + return SlsqpOptimizationResult( + x=result.x, + fval=result.fval, + variables=result.variables, + status=self._get_feasibility_status(problem, result.x), + ) diff --git a/qiskit_optimization/algorithms/warm_start_qaoa_optimizer.py b/qiskit_optimization/algorithms/warm_start_qaoa_optimizer.py index 2c294de1f..360bc5c0b 100644 --- a/qiskit_optimization/algorithms/warm_start_qaoa_optimizer.py +++ b/qiskit_optimization/algorithms/warm_start_qaoa_optimizer.py @@ -21,8 +21,15 @@ from qiskit.algorithms import QAOA from qiskit.circuit import Parameter -from .minimum_eigen_optimizer import MinimumEigenOptimizer, MinimumEigenOptimizationResult -from .optimization_algorithm import OptimizationAlgorithm, OptimizationResultStatus, SolutionSample +from .minimum_eigen_optimizer import ( + MinimumEigenOptimizer, + MinimumEigenOptimizationResult, +) +from .optimization_algorithm import ( + OptimizationAlgorithm, + OptimizationResultStatus, + SolutionSample, +) from ..converters.quadratic_program_converter import QuadraticProgramConverter from ..exceptions import QiskitOptimizationError from ..problems.quadratic_program import QuadraticProgram @@ -33,8 +40,9 @@ class BaseAggregator(ABC): """A base abstract class for aggregates results""" @abstractmethod - def aggregate(self, results: List[MinimumEigenOptimizationResult]) \ - -> List[SolutionSample]: + def aggregate( + self, results: List[MinimumEigenOptimizationResult] + ) -> List[SolutionSample]: """ Aggregates the results. @@ -50,8 +58,9 @@ def aggregate(self, results: List[MinimumEigenOptimizationResult]) \ class MeanAggregator(BaseAggregator): """Aggregates the results by averaging the probability of each sample.""" - def aggregate(self, results: List[MinimumEigenOptimizationResult]) \ - -> List[SolutionSample]: + def aggregate( + self, results: List[MinimumEigenOptimizationResult] + ) -> List[SolutionSample]: """ Args: results: List of result objects that need to be combined. @@ -74,7 +83,11 @@ def _from_string(string) -> np.ndarray: for result in results: for sample in result.samples: # state, fval, prob = sample[0], sample[1], sample[2] - state, fval, prob = _to_string(sample.x), sample.fval, sample.probability + state, fval, prob = ( + _to_string(sample.x), + sample.fval, + sample.probability, + ) if state in dict_samples: dict_samples[state] = (fval, dict_samples[state][1] + prob) else: @@ -85,10 +98,12 @@ def _from_string(string) -> np.ndarray: num_results = len(results) for state in dict_samples: # sample = (state, dict_samples[state][0], dict_samples[state][1] / num_results) - sample = SolutionSample(x=_from_string(state), - fval=dict_samples[state][0], - probability=dict_samples[state][1] / num_results, - status=OptimizationResultStatus.SUCCESS) + sample = SolutionSample( + x=_from_string(state), + fval=dict_samples[state][0], + probability=dict_samples[state][1] / num_results, + status=OptimizationResultStatus.SUCCESS, + ) aggregated_samples.append(sample) return aggregated_samples @@ -113,10 +128,11 @@ def __init__(self, epsilon: float) -> None: Raises: QiskitOptimizationError: if ``epsilon`` is not in the range [0, 0.5]. """ - if epsilon < 0. or epsilon > 0.5: + if epsilon < 0.0 or epsilon > 0.5: raise QiskitOptimizationError( f"Epsilon for warm-start QAOA needs to be between 0 and 0.5, " - f"actual value: {epsilon}") + f"actual value: {epsilon}" + ) self._epsilon = epsilon def create_initial_variables(self, solution: np.ndarray) -> List[float]: @@ -134,8 +150,8 @@ def create_initial_variables(self, solution: np.ndarray) -> List[float]: for variable in solution: if variable < self._epsilon: initial_variables.append(self._epsilon) - elif variable > 1. - self._epsilon: - initial_variables.append(1. - self._epsilon) + elif variable > 1.0 - self._epsilon: + initial_variables.append(1.0 - self._epsilon) else: initial_variables.append(variable) @@ -193,19 +209,21 @@ class WarmStartQAOAOptimizer(MinimumEigenOptimizer): """ - def __init__(self, - pre_solver: OptimizationAlgorithm, - relax_for_pre_solver: bool, - qaoa: QAOA, - epsilon: float = 0.25, - num_initial_solutions: int = 1, - warm_start_factory: Optional[WarmStartQAOAFactory] = None, - aggregator: Optional[BaseAggregator] = None, - penalty: Optional[float] = None, - converters: Optional[Union[QuadraticProgramConverter, - List[QuadraticProgramConverter]]] = None - ) -> None: - """ Initializes the optimizer. For correct initialization either + def __init__( + self, + pre_solver: OptimizationAlgorithm, + relax_for_pre_solver: bool, + qaoa: QAOA, + epsilon: float = 0.25, + num_initial_solutions: int = 1, + warm_start_factory: Optional[WarmStartQAOAFactory] = None, + aggregator: Optional[BaseAggregator] = None, + penalty: Optional[float] = None, + converters: Optional[ + Union[QuadraticProgramConverter, List[QuadraticProgramConverter]] + ] = None, + ) -> None: + """Initializes the optimizer. For correct initialization either ``epsilon`` or ``circuit_factory`` must be passed. If only ``epsilon`` is specified (either an explicit value or the default one), then an instance of :class:`~qiskit.optimization.algorithms.WarmStartQAOACircuitFactory` is created. @@ -240,10 +258,11 @@ def __init__(self, Raises: QiskitOptimizationError: if ``epsilon`` is not in the range [0, 0.5]. """ - if epsilon < 0. or epsilon > 0.5: + if epsilon < 0.0 or epsilon > 0.5: raise QiskitOptimizationError( f"Epsilon for warm-start QAOA needs to be between 0 and 0.5, " - f"actual value: {epsilon}") + f"actual value: {epsilon}" + ) self._pre_solver = pre_solver self._relax_for_pre_solver = relax_for_pre_solver @@ -293,8 +312,10 @@ def solve(self, problem: QuadraticProgram) -> MinimumEigenOptimizationResult: opt_result = self._pre_solver.solve(pre_solver_problem) if opt_result.status != OptimizationResultStatus.SUCCESS: - raise QiskitOptimizationError(f"Presolver returned status {opt_result.status}, " - f"the problem can't be solved") + raise QiskitOptimizationError( + f"Presolver returned status {opt_result.status}, " + f"the problem can't be solved" + ) # we pick only a certain number of the pre-solved solutions. num_pre_solutions = min(self._num_initial_solutions, len(opt_result.samples)) @@ -306,28 +327,39 @@ def solve(self, problem: QuadraticProgram) -> MinimumEigenOptimizationResult: results: List[MinimumEigenOptimizationResult] = [] for pre_solution in pre_solutions: # Set the solver using the result of the pre-solver. - initial_variables = self._warm_start_factory.create_initial_variables(pre_solution.x) - self._qaoa.initial_state = \ - self._warm_start_factory.create_initial_state(initial_variables) + initial_variables = self._warm_start_factory.create_initial_variables( + pre_solution.x + ) + self._qaoa.initial_state = self._warm_start_factory.create_initial_state( + initial_variables + ) self._qaoa.mixer = self._warm_start_factory.create_mixer(initial_variables) # approximate ground state of operator using min eigen solver. - results.append(self._solve_internal(operator, offset, converted_problem, problem)) + results.append( + self._solve_internal(operator, offset, converted_problem, problem) + ) if len(results) == 1: # there's no need to call _interpret, it is already done by MinimumEigenOptimizer return results[0] else: samples = self._aggregator.aggregate(results) - samples.sort(key=lambda sample: converted_problem.objective.sense.value * sample.fval) + samples.sort( + key=lambda sample: converted_problem.objective.sense.value * sample.fval + ) # translate result back to the original variables - return cast(MinimumEigenOptimizationResult, - self._interpret(x=samples[0].x, - problem=problem, - converters=self._converters, - result_class=MinimumEigenOptimizationResult, - samples=samples)) + return cast( + MinimumEigenOptimizationResult, + self._interpret( + x=samples[0].x, + problem=problem, + converters=self._converters, + result_class=MinimumEigenOptimizationResult, + samples=samples, + ), + ) @staticmethod def _relax_problem(problem: QuadraticProgram) -> QuadraticProgram: diff --git a/qiskit_optimization/applications/__init__.py b/qiskit_optimization/applications/__init__.py index 6a0cb15d2..cf9ff3e90 100644 --- a/qiskit_optimization/applications/__init__.py +++ b/qiskit_optimization/applications/__init__.py @@ -61,6 +61,17 @@ from .vehicle_routing import VehicleRouting from .vertex_cover import VertexCover -_all__ = ["Clique", "ExactCover", "GraphOptimizationApplication", "Knapsack", - "Maxcut", "NumberPartition", "OptimizationApplication", "SetPacking", "StableSet", - "Tsp", "VehicleRouting", "VertexCover"] +_all__ = [ + "Clique", + "ExactCover", + "GraphOptimizationApplication", + "Knapsack", + "Maxcut", + "NumberPartition", + "OptimizationApplication", + "SetPacking", + "StableSet", + "Tsp", + "VehicleRouting", + "VertexCover", +] diff --git a/qiskit_optimization/applications/clique.py b/qiskit_optimization/applications/clique.py index 41888f518..da7f730c6 100644 --- a/qiskit_optimization/applications/clique.py +++ b/qiskit_optimization/applications/clique.py @@ -30,8 +30,9 @@ class Clique(GraphOptimizationApplication): https://en.wikipedia.org/wiki/Clique_(graph_theory) """ - def __init__(self, graph: Union[nx.Graph, np.ndarray, List], - size: Optional[int] = None) -> None: + def __init__( + self, graph: Union[nx.Graph, np.ndarray, List], size: Optional[int] = None + ) -> None: """ Args: graph: A graph representing a clique problem. It can be specified directly as a @@ -54,9 +55,9 @@ def to_quadratic_program(self) -> QuadraticProgram: """ complement_g = nx.complement(self._graph) - mdl = Model(name='Clique') + mdl = Model(name="Clique") n = self._graph.number_of_nodes() - x = {i: mdl.binary_var(name='x_{0}'.format(i)) for i in range(n)} + x = {i: mdl.binary_var(name="x_{0}".format(i)) for i in range(n)} for w, v in complement_g.edges: mdl.add_constraint(x[w] + x[v] <= 1) if self.size is None: @@ -83,8 +84,11 @@ def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[int]: clique.append(i) return clique - def _draw_result(self, result: Union[OptimizationResult, np.ndarray], - pos: Optional[Dict[int, np.ndarray]] = None) -> None: + def _draw_result( + self, + result: Union[OptimizationResult, np.ndarray], + pos: Optional[Dict[int, np.ndarray]] = None, + ) -> None: """Draw the result with colors Args: @@ -98,7 +102,7 @@ def _node_colors(self, x: np.ndarray) -> List[str]: # Return a list of strings for draw. # Color a node with red when the corresponding variable is 1. # Otherwise color it with dark gray. - return ['r' if x[node] else 'darkgrey' for node in self._graph.nodes] + return ["r" if x[node] else "darkgrey" for node in self._graph.nodes] @property def size(self) -> int: diff --git a/qiskit_optimization/applications/exact_cover.py b/qiskit_optimization/applications/exact_cover.py index 8160753c8..1e560a829 100644 --- a/qiskit_optimization/applications/exact_cover.py +++ b/qiskit_optimization/applications/exact_cover.py @@ -48,17 +48,23 @@ def to_quadratic_program(self) -> QuadraticProgram: The :class:`~qiskit_optimization.problems.QuadraticProgram` created from the exact cover instance. """ - mdl = Model(name='Exact cover') - x = {i: mdl.binary_var(name='x_{0}'.format(i)) for i in range(len(self._subsets))} + mdl = Model(name="Exact cover") + x = { + i: mdl.binary_var(name="x_{0}".format(i)) for i in range(len(self._subsets)) + } mdl.minimize(mdl.sum(x[i] for i in x)) for element in self._set: - mdl.add_constraint(mdl.sum(x[i] for i, sub in enumerate(self._subsets) - if element in sub) == 1) + mdl.add_constraint( + mdl.sum(x[i] for i, sub in enumerate(self._subsets) if element in sub) + == 1 + ) op = QuadraticProgram() op.from_docplex(mdl) return op - def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[int]]: + def interpret( + self, result: Union[OptimizationResult, np.ndarray] + ) -> List[List[int]]: """Interpret a result as a list of subsets Args: diff --git a/qiskit_optimization/applications/graph_optimization_application.py b/qiskit_optimization/applications/graph_optimization_application.py index 80108a720..467039afa 100644 --- a/qiskit_optimization/applications/graph_optimization_application.py +++ b/qiskit_optimization/applications/graph_optimization_application.py @@ -44,8 +44,11 @@ def __init__(self, graph: Union[nx.Graph, np.ndarray, List]) -> None: # The view of the graph is stored which means the graph can not be changed. self._graph = nx.Graph(graph).copy(as_view=True) - def draw(self, result: Optional[Union[OptimizationResult, np.ndarray]] = None, - pos: Optional[Dict[int, np.ndarray]] = None) -> None: + def draw( + self, + result: Optional[Union[OptimizationResult, np.ndarray]] = None, + pos: Optional[Dict[int, np.ndarray]] = None, + ) -> None: """Draw a graph with the result. When the result is None, draw an original graph without colors. @@ -59,7 +62,8 @@ def draw(self, result: Optional[Union[OptimizationResult, np.ndarray]] = None, raise MissingOptionalLibraryError( libname="matplotlib", name="GraphOptimizationApplication", - pip_install="pip install 'qiskit-optimization[matplotlib]'") + pip_install="pip install 'qiskit-optimization[matplotlib]'", + ) if result is None: nx.draw(self._graph, pos=pos, with_labels=True) @@ -67,8 +71,11 @@ def draw(self, result: Optional[Union[OptimizationResult, np.ndarray]] = None, self._draw_result(result, pos) @abstractmethod - def _draw_result(self, result: Union[OptimizationResult, np.ndarray], - pos: Optional[Dict[int, np.ndarray]] = None) -> None: + def _draw_result( + self, + result: Union[OptimizationResult, np.ndarray], + pos: Optional[Dict[int, np.ndarray]] = None, + ) -> None: """Draw the result with colors Args: @@ -87,7 +94,9 @@ def graph(self) -> nx.Graph: return self._graph @staticmethod - def random_graph(num_nodes: int, num_edges: int, seed: Optional[int] = None) -> nx.Graph: + def random_graph( + num_nodes: int, num_edges: int, seed: Optional[int] = None + ) -> nx.Graph: """ Args: diff --git a/qiskit_optimization/applications/graph_partition.py b/qiskit_optimization/applications/graph_partition.py index 6d6e6391b..3aaddb9df 100644 --- a/qiskit_optimization/applications/graph_partition.py +++ b/qiskit_optimization/applications/graph_partition.py @@ -38,20 +38,24 @@ def to_quadratic_program(self) -> QuadraticProgram: The :class:`~qiskit_optimization.problems.QuadraticProgram` created from the graph partition instance. """ - mdl = Model(name='Graph partition') + mdl = Model(name="Graph partition") n = self._graph.number_of_nodes() - x = {i: mdl.binary_var(name='x_{0}'.format(i)) for i in range(n)} + x = {i: mdl.binary_var(name="x_{0}".format(i)) for i in range(n)} for w, v in self._graph.edges: - self._graph.edges[w, v].setdefault('weight', 1) - objective = mdl.sum(self._graph.edges[i, j]['weight'] * - (x[i] + x[j] - 2*x[i]*x[j]) for i, j in self._graph.edges) + self._graph.edges[w, v].setdefault("weight", 1) + objective = mdl.sum( + self._graph.edges[i, j]["weight"] * (x[i] + x[j] - 2 * x[i] * x[j]) + for i, j in self._graph.edges + ) mdl.minimize(objective) - mdl.add_constraint(mdl.sum([x[i] for i in x]) == n//2) + mdl.add_constraint(mdl.sum([x[i] for i in x]) == n // 2) op = QuadraticProgram() op.from_docplex(mdl) return op - def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[int]]: + def interpret( + self, result: Union[OptimizationResult, np.ndarray] + ) -> List[List[int]]: """Interpret a result as a list of node indices Args: @@ -69,8 +73,11 @@ def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[ partition[1].append(i) return partition - def _draw_result(self, result: Union[OptimizationResult, np.ndarray], - pos: Optional[Dict[int, np.ndarray]] = None) -> None: + def _draw_result( + self, + result: Union[OptimizationResult, np.ndarray], + pos: Optional[Dict[int, np.ndarray]] = None, + ) -> None: """Draw the result with colors Args: @@ -84,4 +91,4 @@ def _node_colors(self, x: np.ndarray) -> List[str]: # Return a list of strings for draw. # Color a node with red when the corresponding variable is 1. # Otherwise color it with blue. - return ['r' if x[node] else 'b' for node in self._graph.nodes] + return ["r" if x[node] else "b" for node in self._graph.nodes] diff --git a/qiskit_optimization/applications/knapsack.py b/qiskit_optimization/applications/knapsack.py index 40b1372a5..faba86604 100644 --- a/qiskit_optimization/applications/knapsack.py +++ b/qiskit_optimization/applications/knapsack.py @@ -48,10 +48,14 @@ def to_quadratic_program(self) -> QuadraticProgram: The :class:`~qiskit_optimization.problems.QuadraticProgram` created from the knapsack problem instance. """ - mdl = Model(name='Knapsack') - x = {i: mdl.binary_var(name='x_{0}'.format(i)) for i in range(len(self._values))} - mdl.maximize(mdl.sum(self._values[i]*x[i] for i in x)) - mdl.add_constraint(mdl.sum(self._weights[i] * x[i] for i in x) <= self._max_weight) + mdl = Model(name="Knapsack") + x = { + i: mdl.binary_var(name="x_{0}".format(i)) for i in range(len(self._values)) + } + mdl.maximize(mdl.sum(self._values[i] * x[i] for i in x)) + mdl.add_constraint( + mdl.sum(self._weights[i] * x[i] for i in x) <= self._max_weight + ) op = QuadraticProgram() op.from_docplex(mdl) return op diff --git a/qiskit_optimization/applications/max_cut.py b/qiskit_optimization/applications/max_cut.py index 28c53f3f7..97ee237a5 100644 --- a/qiskit_optimization/applications/max_cut.py +++ b/qiskit_optimization/applications/max_cut.py @@ -39,21 +39,28 @@ def to_quadratic_program(self) -> QuadraticProgram: The :class:`~qiskit_optimization.problems.QuadraticProgram` created from the Max-cut problem instance. """ - mdl = Model(name='Max-cut') - x = {i: mdl.binary_var(name='x_{0}'.format(i)) - for i in range(self._graph.number_of_nodes())} + mdl = Model(name="Max-cut") + x = { + i: mdl.binary_var(name="x_{0}".format(i)) + for i in range(self._graph.number_of_nodes()) + } for w, v in self._graph.edges: - self._graph.edges[w, v].setdefault('weight', 1) - objective = mdl.sum(self._graph.edges[i, j]['weight'] * x[i] - * (1 - x[j]) + self._graph.edges[i, j]['weight'] * x[j] - * (1 - x[i]) for i, j in self._graph.edges) + self._graph.edges[w, v].setdefault("weight", 1) + objective = mdl.sum( + self._graph.edges[i, j]["weight"] * x[i] * (1 - x[j]) + + self._graph.edges[i, j]["weight"] * x[j] * (1 - x[i]) + for i, j in self._graph.edges + ) mdl.maximize(objective) op = QuadraticProgram() op.from_docplex(mdl) return op - def _draw_result(self, result: Union[OptimizationResult, np.ndarray], - pos: Optional[Dict[int, np.ndarray]] = None) -> None: + def _draw_result( + self, + result: Union[OptimizationResult, np.ndarray], + pos: Optional[Dict[int, np.ndarray]] = None, + ) -> None: """Draw the result with colors Args: @@ -63,7 +70,9 @@ def _draw_result(self, result: Union[OptimizationResult, np.ndarray], x = self._result_to_x(result) nx.draw(self._graph, node_color=self._node_color(x), pos=pos, with_labels=True) - def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[int]]: + def interpret( + self, result: Union[OptimizationResult, np.ndarray] + ) -> List[List[int]]: """Interpret a result as two lists of node indices Args: @@ -85,7 +94,7 @@ def _node_color(self, x: np.ndarray) -> List[str]: # Return a list of strings for draw. # Color a node with red when the corresponding variable is 1. # Otherwise color it with blue. - return ['r' if value == 0 else 'b' for value in x] + return ["r" if value == 0 else "b" for value in x] @staticmethod def parse_gset_format(filename: str) -> np.ndarray: @@ -103,7 +112,8 @@ def parse_gset_format(filename: str) -> np.ndarray: m = -1 count = 0 for line in infile: - v = map(lambda e: int(e), line.split()) # pylint: disable=unnecessary-lambda + # pylint: disable=unnecessary-lambda + v = map(lambda e: int(e), line.split()) if header: n, m = v w = np.zeros((n, n)) diff --git a/qiskit_optimization/applications/number_partition.py b/qiskit_optimization/applications/number_partition.py index b8127ba0c..b58e1334a 100644 --- a/qiskit_optimization/applications/number_partition.py +++ b/qiskit_optimization/applications/number_partition.py @@ -45,15 +45,22 @@ def to_quadratic_program(self) -> QuadraticProgram: The :class:`~qiskit_optimization.problems.QuadraticProgram` created from the number partitioning problem instance. """ - mdl = Model(name='Number partitioning') - x = {i: mdl.binary_var(name='x_{0}'.format(i)) for i in range(len(self._number_set))} - mdl.add_constraint(mdl.sum(num * (-2 * x[i] + 1) - for i, num in enumerate(self._number_set)) == 0) + mdl = Model(name="Number partitioning") + x = { + i: mdl.binary_var(name="x_{0}".format(i)) + for i in range(len(self._number_set)) + } + mdl.add_constraint( + mdl.sum(num * (-2 * x[i] + 1) for i, num in enumerate(self._number_set)) + == 0 + ) op = QuadraticProgram() op.from_docplex(mdl) return op - def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[int]]: + def interpret( + self, result: Union[OptimizationResult, np.ndarray] + ) -> List[List[int]]: """Interpret a result as a list of subsets Args: diff --git a/qiskit_optimization/applications/optimization_application.py b/qiskit_optimization/applications/optimization_application.py index af6f9510e..c26377822 100644 --- a/qiskit_optimization/applications/optimization_application.py +++ b/qiskit_optimization/applications/optimization_application.py @@ -52,8 +52,10 @@ def _result_to_x(self, result: Union[OptimizationResult, np.ndarray]) -> np.ndar elif isinstance(result, np.ndarray): x = result else: - raise TypeError("Unsupported format of result. Provide an OptimizationResult or a", - "binary array using np.ndarray instead of {}".format(type(result))) + raise TypeError( + "Unsupported format of result. Provide an OptimizationResult or a", + "binary array using np.ndarray instead of {}".format(type(result)), + ) return x @staticmethod diff --git a/qiskit_optimization/applications/set_packing.py b/qiskit_optimization/applications/set_packing.py index 88442911e..fd26cc7ce 100644 --- a/qiskit_optimization/applications/set_packing.py +++ b/qiskit_optimization/applications/set_packing.py @@ -49,17 +49,23 @@ def to_quadratic_program(self) -> QuadraticProgram: The :class:`~qiskit_optimization.problems.QuadraticProgram` created from the set packing instance. """ - mdl = Model(name='Set packing') - x = {i: mdl.binary_var(name='x_{0}'.format(i)) for i in range(len(self._subsets))} + mdl = Model(name="Set packing") + x = { + i: mdl.binary_var(name="x_{0}".format(i)) for i in range(len(self._subsets)) + } mdl.maximize(mdl.sum(x[i] for i in x)) for element in self._set: - mdl.add_constraint(mdl.sum(x[i] for i, sub in enumerate(self._subsets) - if element in sub) <= 1) + mdl.add_constraint( + mdl.sum(x[i] for i, sub in enumerate(self._subsets) if element in sub) + <= 1 + ) op = QuadraticProgram() op.from_docplex(mdl) return op - def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[int]]: + def interpret( + self, result: Union[OptimizationResult, np.ndarray] + ) -> List[List[int]]: """Interpret a result as a list of subsets Args: diff --git a/qiskit_optimization/applications/stable_set.py b/qiskit_optimization/applications/stable_set.py index 8b5d3fd4e..ae2fb8e07 100644 --- a/qiskit_optimization/applications/stable_set.py +++ b/qiskit_optimization/applications/stable_set.py @@ -39,11 +39,11 @@ def to_quadratic_program(self) -> QuadraticProgram: The :class:`~qiskit_optimization.problems.QuadraticProgram` created from the stable set instance. """ - mdl = Model(name='Stable set') + mdl = Model(name="Stable set") n = self._graph.number_of_nodes() - x = {i: mdl.binary_var(name='x_{0}'.format(i)) for i in range(n)} + x = {i: mdl.binary_var(name="x_{0}".format(i)) for i in range(n)} for w, v in self._graph.edges: - self._graph.edges[w, v].setdefault('weight', 1) + self._graph.edges[w, v].setdefault("weight", 1) objective = mdl.sum(x[i] for i in x) for w, v in self._graph.edges: mdl.add_constraint(x[w] + x[v] <= 1) @@ -68,8 +68,11 @@ def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[int]: stable_set.append(i) return stable_set - def _draw_result(self, result: Union[OptimizationResult, np.ndarray], - pos: Optional[Dict[int, np.ndarray]] = None) -> None: + def _draw_result( + self, + result: Union[OptimizationResult, np.ndarray], + pos: Optional[Dict[int, np.ndarray]] = None, + ) -> None: """Draw the result with colors Args: @@ -83,4 +86,4 @@ def _node_colors(self, x: np.ndarray): # Return a list of strings for draw. # Color a node with red when the corresponding variable is 1. # Otherwise color it with dark gray. - return ['r' if x[node] == 1 else 'darkgrey' for node in self._graph.nodes] + return ["r" if x[node] == 1 else "darkgrey" for node in self._graph.nodes] diff --git a/qiskit_optimization/applications/tsp.py b/qiskit_optimization/applications/tsp.py index a24c4aace..168f9ce50 100644 --- a/qiskit_optimization/applications/tsp.py +++ b/qiskit_optimization/applications/tsp.py @@ -41,12 +41,20 @@ def to_quadratic_program(self) -> QuadraticProgram: The :class:`~qiskit_optimization.problems.QuadraticProgram` created from the traveling salesman problem instance. """ - mdl = Model(name='TSP') + mdl = Model(name="TSP") n = self._graph.number_of_nodes() - x = {(i, k): mdl.binary_var(name='x_{0}_{1}'.format(i, k)) - for i in range(n) for k in range(n)} - tsp_func = mdl.sum(self._graph.edges[i, j]['weight'] * x[(i, k)] * x[(j, (k+1) % n)] - for i in range(n) for j in range(n) for k in range(n) if i != j) + x = { + (i, k): mdl.binary_var(name="x_{0}_{1}".format(i, k)) + for i in range(n) + for k in range(n) + } + tsp_func = mdl.sum( + self._graph.edges[i, j]["weight"] * x[(i, k)] * x[(j, (k + 1) % n)] + for i in range(n) + for j in range(n) + for k in range(n) + if i != j + ) mdl.minimize(tsp_func) for i in range(n): mdl.add_constraint(mdl.sum(x[(i, k)] for k in range(n)) == 1) @@ -56,8 +64,9 @@ def to_quadratic_program(self) -> QuadraticProgram: op.from_docplex(mdl) return op - def interpret(self, - result: Union[OptimizationResult, np.ndarray]) -> List[Union[int, List[int]]]: + def interpret( + self, result: Union[OptimizationResult, np.ndarray] + ) -> List[Union[int, List[int]]]: """Interpret a result as a list of node indices Args: @@ -80,8 +89,11 @@ def interpret(self, route.append(p_step) return route - def _draw_result(self, result: Union[OptimizationResult, np.ndarray], - pos: Optional[Dict[int, np.ndarray]] = None) -> None: + def _draw_result( + self, + result: Union[OptimizationResult, np.ndarray], + pos: Optional[Dict[int, np.ndarray]] = None, + ) -> None: """Draw the result with colors Args: @@ -94,17 +106,21 @@ def _draw_result(self, result: Union[OptimizationResult, np.ndarray], self._graph, pos, edgelist=self._edgelist(x), - width=8, alpha=0.5, edge_color="tab:red", - ) + width=8, + alpha=0.5, + edge_color="tab:red", + ) def _edgelist(self, x: np.ndarray): # Arrange route and return the list of the edges for the edge list of nx.draw_networkx_edges route = self.interpret(x) - return [(route[i], route[(i+1) % len(route)]) for i in range(len(route))] + return [(route[i], route[(i + 1) % len(route)]) for i in range(len(route))] @staticmethod # pylint: disable=undefined-variable - def create_random_instance(n: int, low: int = 0, high: int = 100, seed: int = None) -> 'Tsp': + def create_random_instance( + n: int, low: int = 0, high: int = 100, seed: int = None + ) -> "Tsp": """Create a random instance of the traveling salesman problem Args: @@ -119,15 +135,19 @@ def create_random_instance(n: int, low: int = 0, high: int = 100, seed: int = No if seed: algorithm_globals.random_seed = seed coord = algorithm_globals.random.uniform(low, high, (n, 2)) - pos = {i: (coord_[0], coord_[1])for i, coord_ in enumerate(coord)} - graph = nx.random_geometric_graph(n, np.hypot(high-low, high-low)+1, pos=pos) + pos = {i: (coord_[0], coord_[1]) for i, coord_ in enumerate(coord)} + graph = nx.random_geometric_graph( + n, np.hypot(high - low, high - low) + 1, pos=pos + ) for w, v in graph.edges: - delta = [graph.nodes[w]['pos'][i] - graph.nodes[v]['pos'][i] for i in range(2)] - graph.edges[w, v]['weight'] = np.rint(np.hypot(delta[0], delta[1])) + delta = [ + graph.nodes[w]["pos"][i] - graph.nodes[v]["pos"][i] for i in range(2) + ] + graph.edges[w, v]["weight"] = np.rint(np.hypot(delta[0], delta[1])) return Tsp(graph) @staticmethod - def parse_tsplib_format(filename: str) -> 'Tsp': + def parse_tsplib_format(filename: str) -> "Tsp": """Read a graph in TSPLIB format from file and return a Tsp instance. Args: @@ -140,30 +160,34 @@ def parse_tsplib_format(filename: str) -> 'Tsp': Returns: A Tsp instance data. """ - name = '' + name = "" coord = [] # type: ignore with open(filename) as infile: coord_section = False for line in infile: - if line.startswith('NAME'): - name = line.split(':')[1] + if line.startswith("NAME"): + name = line.split(":")[1] name.strip() - elif line.startswith('TYPE'): - typ = line.split(':')[1] + elif line.startswith("TYPE"): + typ = line.split(":")[1] typ.strip() - if typ != 'TSP': + if typ != "TSP": raise QiskitOptimizationError( - "This supports only \"TSP\" type. Actual: {}".format(typ)) - elif line.startswith('DIMENSION'): - dim = int(line.split(':')[1]) + 'This supports only "TSP" type. Actual: {}'.format(typ) + ) + elif line.startswith("DIMENSION"): + dim = int(line.split(":")[1]) coord = np.zeros((dim, 2)) # type: ignore - elif line.startswith('EDGE_WEIGHT_TYPE'): - typ = line.split(':')[1] + elif line.startswith("EDGE_WEIGHT_TYPE"): + typ = line.split(":")[1] typ.strip() - if typ != 'EUC_2D': + if typ != "EUC_2D": raise QiskitOptimizationError( - "This supports only \"EUC_2D\" edge weight. Actual: {}".format(typ)) - elif line.startswith('NODE_COORD_SECTION'): + 'This supports only "EUC_2D" edge weight. Actual: {}'.format( + typ + ) + ) + elif line.startswith("NODE_COORD_SECTION"): coord_section = True elif coord_section: v = line.split() @@ -176,11 +200,14 @@ def parse_tsplib_format(filename: str) -> 'Tsp': y_max = max(coord_[1] for coord_ in coord) y_min = min(coord_[1] for coord_ in coord) - graph = nx.random_geometric_graph(len(coord), - np.hypot(x_max-x_min, y_max-y_min)+1, pos=coord) + graph = nx.random_geometric_graph( + len(coord), np.hypot(x_max - x_min, y_max - y_min) + 1, pos=coord + ) for w, v in graph.edges: - delta = [graph.nodes[w]['pos'][i] - graph.nodes[v]['pos'][i] for i in range(2)] - graph.edges[w, v]['weight'] = np.rint(np.hypot(delta[0], delta[1])) + delta = [ + graph.nodes[w]["pos"][i] - graph.nodes[v]["pos"][i] for i in range(2) + ] + graph.edges[w, v]["weight"] = np.rint(np.hypot(delta[0], delta[1])) return Tsp(graph) @staticmethod diff --git a/qiskit_optimization/applications/vehicle_routing.py b/qiskit_optimization/applications/vehicle_routing.py index 28a195046..ad5911368 100644 --- a/qiskit_optimization/applications/vehicle_routing.py +++ b/qiskit_optimization/applications/vehicle_routing.py @@ -32,8 +32,12 @@ class VehicleRouting(GraphOptimizationApplication): [1]: "Vehicle routing problem", https://en.wikipedia.org/wiki/Vehicle_routing_problem """ - def __init__(self, graph: Union[nx.Graph, np.ndarray, List], - num_vehicles: int = 2, depot: int = 0) -> None: + def __init__( + self, + graph: Union[nx.Graph, np.ndarray, List], + num_vehicles: int = 2, + depot: int = 0, + ) -> None: """ Args: graph: A graph representing a vehicle routing problem. It can be specified directly as a @@ -54,15 +58,21 @@ def to_quadratic_program(self) -> QuadraticProgram: The :class:`~qiskit_optimization.problems.QuadraticProgram` created from the vehicle routing problem instance. """ - mdl = Model(name='Vehicle routing') + mdl = Model(name="Vehicle routing") n = self._graph.number_of_nodes() x = {} for i in range(n): for j in range(n): if i != j: - x[(i, j)] = mdl.binary_var(name='x_{0}_{1}'.format(i, j)) - mdl.minimize(mdl.sum(self._graph.edges[i, j]['weight']*x[(i, j)] - for i in range(n) for j in range(n) if i != j)) + x[(i, j)] = mdl.binary_var(name="x_{0}_{1}".format(i, j)) + mdl.minimize( + mdl.sum( + self._graph.edges[i, j]["weight"] * x[(i, j)] + for i in range(n) + for j in range(n) + if i != j + ) + ) # Only 1 edge goes out from each node for i in range(n): if i != self.depot: @@ -72,26 +82,33 @@ def to_quadratic_program(self) -> QuadraticProgram: if j != self.depot: mdl.add_constraint(mdl.sum(x[i, j] for i in range(n) if i != j) == 1) # For the depot node - mdl.add_constraint(mdl.sum(x[i, self.depot] - for i in range(n) if i != self.depot) == self.num_vehicles) - mdl.add_constraint(mdl.sum(x[self.depot, j] - for j in range(n) if j != self.depot) == self.num_vehicles) + mdl.add_constraint( + mdl.sum(x[i, self.depot] for i in range(n) if i != self.depot) + == self.num_vehicles + ) + mdl.add_constraint( + mdl.sum(x[self.depot, j] for j in range(n) if j != self.depot) + == self.num_vehicles + ) # To eliminate sub-routes node_list = [i for i in range(n) if i != self.depot] clique_set = [] - for i in range(2, len(node_list)+1): + for i in range(2, len(node_list) + 1): for comb in itertools.combinations(node_list, i): clique_set.append(list(comb)) for clique in clique_set: - mdl.add_constraint(mdl.sum(x[(i, j)] - for i in clique - for j in clique if i != j) <= len(clique) - 1) + mdl.add_constraint( + mdl.sum(x[(i, j)] for i in clique for j in clique if i != j) + <= len(clique) - 1 + ) op = QuadraticProgram() op.from_docplex(mdl) return op - def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[List[int]]]: + def interpret( + self, result: Union[OptimizationResult, np.ndarray] + ) -> List[List[List[int]]]: """Interpret a result as a list of the routes for each vehicle Args: @@ -132,8 +149,11 @@ def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[List[ return route_list - def _draw_result(self, result: Union[OptimizationResult, np.ndarray], - pos: Optional[Dict[int, np.ndarray]] = None) -> None: + def _draw_result( + self, + result: Union[OptimizationResult, np.ndarray], + pos: Optional[Dict[int, np.ndarray]] = None, + ) -> None: """Draw the result with colors Args: @@ -148,8 +168,11 @@ def _draw_result(self, result: Union[OptimizationResult, np.ndarray], self._graph, pos, edgelist=self._edgelist(route_list), - width=8, alpha=0.5, edge_color=self._edge_color(route_list), edge_cmap=plt.cm.plasma - ) + width=8, + alpha=0.5, + edge_color=self._edge_color(route_list), + edge_cmap=plt.cm.plasma, + ) def _edgelist(self, route_list: List[List[List[int]]]): # Arrange route_list and return the list of the edges for the edge list of @@ -159,7 +182,11 @@ def _edgelist(self, route_list: List[List[List[int]]]): def _edge_color(self, route_list: List[List[List[int]]]): # Arrange route_list and return the list of the colors of each route # for edge_color of nx.draw_networkx_edges - return [k/len(route_list) for k in range(len(route_list)) for edge in route_list[k]] + return [ + k / len(route_list) + for k in range(len(route_list)) + for edge in route_list[k] + ] @property def num_vehicles(self) -> int: @@ -199,8 +226,14 @@ def depot(self, depot: int) -> None: @staticmethod # pylint: disable=undefined-variable - def create_random_instance(n: int, low: int = 0, high: int = 100, seed: Optional[int] = None, - num_vehicle: int = 2, depot: int = 0) -> 'VehicleRouting': + def create_random_instance( + n: int, + low: int = 0, + high: int = 100, + seed: Optional[int] = None, + num_vehicle: int = 2, + depot: int = 0, + ) -> "VehicleRouting": """Create a random instance of the vehicle routing problem. Args: @@ -215,9 +248,15 @@ def create_random_instance(n: int, low: int = 0, high: int = 100, seed: Optional A VehicleRouting instance created from the input information """ random.seed(seed) - pos = {i: (random.randint(low, high), random.randint(low, high)) for i in range(n)} - graph = nx.random_geometric_graph(n, np.hypot(high-low, high-low)+1, pos=pos) + pos = { + i: (random.randint(low, high), random.randint(low, high)) for i in range(n) + } + graph = nx.random_geometric_graph( + n, np.hypot(high - low, high - low) + 1, pos=pos + ) for w, v in graph.edges: - delta = [graph.nodes[w]['pos'][i] - graph.nodes[v]['pos'][i] for i in range(2)] - graph.edges[w, v]['weight'] = np.rint(np.hypot(delta[0], delta[1])) + delta = [ + graph.nodes[w]["pos"][i] - graph.nodes[v]["pos"][i] for i in range(2) + ] + graph.edges[w, v]["weight"] = np.rint(np.hypot(delta[0], delta[1])) return VehicleRouting(graph, num_vehicle, depot) diff --git a/qiskit_optimization/applications/vertex_cover.py b/qiskit_optimization/applications/vertex_cover.py index ca4ef9985..a645cd95b 100644 --- a/qiskit_optimization/applications/vertex_cover.py +++ b/qiskit_optimization/applications/vertex_cover.py @@ -38,9 +38,9 @@ def to_quadratic_program(self) -> QuadraticProgram: The :class:`~qiskit_optimization.problems.QuadraticProgram` created from the vertex cover instance. """ - mdl = Model(name='Vertex cover') + mdl = Model(name="Vertex cover") n = self._graph.number_of_nodes() - x = {i: mdl.binary_var(name='x_{0}'.format(i)) for i in range(n)} + x = {i: mdl.binary_var(name="x_{0}".format(i)) for i in range(n)} objective = mdl.sum(x[i] for i in x) for w, v in self._graph.edges: mdl.add_constraint(x[w] + x[v] >= 1) @@ -65,8 +65,11 @@ def interpret(self, result: Union[OptimizationResult, np.ndarray]) -> List[int]: vertex_cover.append(i) return vertex_cover - def _draw_result(self, result: Union[OptimizationResult, np.ndarray], - pos: Optional[Dict[int, np.ndarray]] = None) -> None: + def _draw_result( + self, + result: Union[OptimizationResult, np.ndarray], + pos: Optional[Dict[int, np.ndarray]] = None, + ) -> None: """Draw the result with colors Args: @@ -80,4 +83,4 @@ def _node_colors(self, x: np.ndarray) -> List[str]: # Return a list of strings for draw. # Color a node with red when the corresponding variable is 1. # Otherwise color it with dark gray. - return ['r' if x[node] else 'darkgrey' for node in self._graph.nodes] + return ["r" if x[node] else "darkgrey" for node in self._graph.nodes] diff --git a/qiskit_optimization/converters/inequality_to_equality.py b/qiskit_optimization/converters/inequality_to_equality.py index 1f649a90f..ef5d71785 100644 --- a/qiskit_optimization/converters/inequality_to_equality.py +++ b/qiskit_optimization/converters/inequality_to_equality.py @@ -40,9 +40,9 @@ class InequalityToEquality(QuadraticProgramConverter): >>> problem2 = conv.convert(problem) """ - _delimiter = '@' # users are supposed not to use this character in variable names + _delimiter = "@" # users are supposed not to use this character in variable names - def __init__(self, mode: str = 'auto') -> None: + def __init__(self, mode: str = "auto") -> None: """ Args: mode: To chose the type of slack variables. There are 3 options for mode. @@ -90,7 +90,8 @@ def convert(self, problem: QuadraticProgram) -> QuadraticProgram: ) else: raise QiskitOptimizationError( - "Unsupported variable type {}".format(x.vartype)) + "Unsupported variable type {}".format(x.vartype) + ) # Copy the objective function constant = self._src.objective.constant @@ -109,28 +110,28 @@ def convert(self, problem: QuadraticProgram) -> QuadraticProgram: linear, l_constraint.sense, l_constraint.rhs, l_constraint.name ) elif ( - l_constraint.sense == Constraint.Sense.LE - or l_constraint.sense == Constraint.Sense.GE + l_constraint.sense == Constraint.Sense.LE + or l_constraint.sense == Constraint.Sense.GE ): - if mode == 'integer': + if mode == "integer": self._add_integer_slack_var_linear_constraint( linear, l_constraint.sense, l_constraint.rhs, l_constraint.name ) - elif mode == 'continuous': + elif mode == "continuous": self._add_continuous_slack_var_linear_constraint( linear, l_constraint.sense, l_constraint.rhs, l_constraint.name ) - elif mode == 'auto': + elif mode == "auto": self._add_auto_slack_var_linear_constraint( linear, l_constraint.sense, l_constraint.rhs, l_constraint.name ) else: raise QiskitOptimizationError( - 'Unsupported mode is selected: {}'.format(mode)) + "Unsupported mode is selected: {}".format(mode) + ) else: raise QiskitOptimizationError( - 'Type of sense in {} is not supported'.format( - l_constraint.name) + "Type of sense in {} is not supported".format(l_constraint.name) ) # For quadratic constraints @@ -139,31 +140,47 @@ def convert(self, problem: QuadraticProgram) -> QuadraticProgram: quadratic = q_constraint.quadratic.to_dict(use_name=True) if q_constraint.sense == Constraint.Sense.EQ: self._dst.quadratic_constraint( - linear, quadratic, q_constraint.sense, q_constraint.rhs, q_constraint.name + linear, + quadratic, + q_constraint.sense, + q_constraint.rhs, + q_constraint.name, ) elif ( - q_constraint.sense == Constraint.Sense.LE - or q_constraint.sense == Constraint.Sense.GE + q_constraint.sense == Constraint.Sense.LE + or q_constraint.sense == Constraint.Sense.GE ): - if mode == 'integer': + if mode == "integer": self._add_integer_slack_var_quadratic_constraint( - linear, quadratic, q_constraint.sense, q_constraint.rhs, q_constraint.name, + linear, + quadratic, + q_constraint.sense, + q_constraint.rhs, + q_constraint.name, ) - elif mode == 'continuous': + elif mode == "continuous": self._add_continuous_slack_var_quadratic_constraint( - linear, quadratic, q_constraint.sense, q_constraint.rhs, q_constraint.name, + linear, + quadratic, + q_constraint.sense, + q_constraint.rhs, + q_constraint.name, ) - elif mode == 'auto': + elif mode == "auto": self._add_auto_slack_var_quadratic_constraint( - linear, quadratic, q_constraint.sense, q_constraint.rhs, q_constraint.name, + linear, + quadratic, + q_constraint.sense, + q_constraint.rhs, + q_constraint.name, ) else: raise QiskitOptimizationError( - 'Unsupported mode is selected: {}'.format(mode)) + "Unsupported mode is selected: {}".format(mode) + ) else: raise QiskitOptimizationError( - 'Type of sense in {} is not supported'.format( - q_constraint.name) + "Type of sense in {} is not supported".format(q_constraint.name) ) return self._dst @@ -173,7 +190,8 @@ def _add_integer_slack_var_linear_constraint(self, linear, sense, rhs, name): if self._contains_any_float_value(linear.values()): raise QiskitOptimizationError( '"{0}" contains float coefficients. ' - 'We can not use an integer slack variable for "{0}"'.format(name)) + 'We can not use an integer slack variable for "{0}"'.format(name) + ) # If rhs is float number, round up/down to the nearest integer. new_rhs = rhs @@ -183,7 +201,7 @@ def _add_integer_slack_var_linear_constraint(self, linear, sense, rhs, name): new_rhs = math.ceil(rhs) # Add a new integer variable. - slack_name = name + self._delimiter + 'int_slack' + slack_name = name + self._delimiter + "int_slack" lhs_lb, lhs_ub = self._calc_linear_bounds(linear) @@ -193,19 +211,18 @@ def _add_integer_slack_var_linear_constraint(self, linear, sense, rhs, name): var_ub = new_rhs - lhs_lb if var_ub > 0: sign = 1 - self._dst.integer_var( - name=slack_name, lowerbound=0, upperbound=var_ub) + self._dst.integer_var(name=slack_name, lowerbound=0, upperbound=var_ub) var_added = True elif sense == Constraint.Sense.GE: var_ub = lhs_ub - new_rhs if var_ub > 0: sign = -1 - self._dst.integer_var( - name=slack_name, lowerbound=0, upperbound=var_ub) + self._dst.integer_var(name=slack_name, lowerbound=0, upperbound=var_ub) var_added = True else: raise QiskitOptimizationError( - 'The type of Sense in {} is not supported'.format(name)) + "The type of Sense in {} is not supported".format(name) + ) # Add a new equality constraint. new_linear = copy.deepcopy(linear) @@ -214,7 +231,7 @@ def _add_integer_slack_var_linear_constraint(self, linear, sense, rhs, name): self._dst.linear_constraint(new_linear, "==", new_rhs, name) def _add_continuous_slack_var_linear_constraint(self, linear, sense, rhs, name): - slack_name = name + self._delimiter + 'continuous_slack' + slack_name = name + self._delimiter + "continuous_slack" lhs_lb, lhs_ub = self._calc_linear_bounds(linear) @@ -224,18 +241,21 @@ def _add_continuous_slack_var_linear_constraint(self, linear, sense, rhs, name): if var_ub > 0: sign = 1 self._dst.continuous_var( - name=slack_name, lowerbound=0, upperbound=var_ub) + name=slack_name, lowerbound=0, upperbound=var_ub + ) var_added = True elif sense == Constraint.Sense.GE: var_ub = lhs_ub - rhs if var_ub > 0: sign = -1 self._dst.continuous_var( - name=slack_name, lowerbound=0, upperbound=var_ub) + name=slack_name, lowerbound=0, upperbound=var_ub + ) var_added = True else: raise QiskitOptimizationError( - 'The type of Sense in {} is not supported'.format(name)) + "The type of Sense in {} is not supported".format(name) + ) # Add a new equality constraint. new_linear = copy.deepcopy(linear) @@ -246,20 +266,22 @@ def _add_continuous_slack_var_linear_constraint(self, linear, sense, rhs, name): def _add_auto_slack_var_linear_constraint(self, linear, sense, rhs, name): # If a coefficient that is not integer exist, use a continuous slack variable if self._contains_any_float_value(list(linear.values())): - self._add_continuous_slack_var_linear_constraint( - linear, sense, rhs, name) + self._add_continuous_slack_var_linear_constraint(linear, sense, rhs, name) # Else use an integer slack variable else: - self._add_integer_slack_var_linear_constraint( - linear, sense, rhs, name) + self._add_integer_slack_var_linear_constraint(linear, sense, rhs, name) - def _add_integer_slack_var_quadratic_constraint(self, linear, quadratic, sense, rhs, name): + def _add_integer_slack_var_quadratic_constraint( + self, linear, quadratic, sense, rhs, name + ): # If a coefficient that is not integer exist, raise an error - if (self._contains_any_float_value(list(linear.values())) - or self._contains_any_float_value(list(quadratic.values()))): + if self._contains_any_float_value( + list(linear.values()) + ) or self._contains_any_float_value(list(quadratic.values())): raise QiskitOptimizationError( '"{0}" contains float coefficients. ' - 'We can not use an integer slack variable for "{0}"'.format(name)) + 'We can not use an integer slack variable for "{0}"'.format(name) + ) # If rhs is float number, round up/down to the nearest integer. new_rhs = rhs @@ -269,7 +291,7 @@ def _add_integer_slack_var_quadratic_constraint(self, linear, quadratic, sense, new_rhs = math.ceil(rhs) # Add a new integer variable. - slack_name = name + self._delimiter + 'int_slack' + slack_name = name + self._delimiter + "int_slack" lhs_lb, lhs_ub = self._calc_quadratic_bounds(linear, quadratic) @@ -279,30 +301,30 @@ def _add_integer_slack_var_quadratic_constraint(self, linear, quadratic, sense, var_ub = new_rhs - lhs_lb if var_ub > 0: sign = 1 - self._dst.integer_var( - name=slack_name, lowerbound=0, upperbound=var_ub) + self._dst.integer_var(name=slack_name, lowerbound=0, upperbound=var_ub) var_added = True elif sense == Constraint.Sense.GE: var_ub = lhs_ub - new_rhs if var_ub > 0: sign = -1 - self._dst.integer_var( - name=slack_name, lowerbound=0, upperbound=var_ub) + self._dst.integer_var(name=slack_name, lowerbound=0, upperbound=var_ub) var_added = True else: raise QiskitOptimizationError( - 'The type of Sense in {} is not supported'.format(name)) + "The type of Sense in {} is not supported".format(name) + ) # Add a new equality constraint. new_linear = copy.deepcopy(linear) if var_added: new_linear[slack_name] = sign - self._dst.quadratic_constraint( - new_linear, quadratic, "==", new_rhs, name) + self._dst.quadratic_constraint(new_linear, quadratic, "==", new_rhs, name) - def _add_continuous_slack_var_quadratic_constraint(self, linear, quadratic, sense, rhs, name): + def _add_continuous_slack_var_quadratic_constraint( + self, linear, quadratic, sense, rhs, name + ): # Add a new continuous variable. - slack_name = name + self._delimiter + 'continuous_slack' + slack_name = name + self._delimiter + "continuous_slack" lhs_lb, lhs_ub = self._calc_quadratic_bounds(linear, quadratic) @@ -313,18 +335,21 @@ def _add_continuous_slack_var_quadratic_constraint(self, linear, quadratic, sens if var_ub > 0: sign = 1 self._dst.continuous_var( - name=slack_name, lowerbound=0, upperbound=var_ub) + name=slack_name, lowerbound=0, upperbound=var_ub + ) var_added = True elif sense == Constraint.Sense.GE: var_ub = lhs_ub - rhs if var_ub > 0: sign = -1 self._dst.continuous_var( - name=slack_name, lowerbound=0, upperbound=lhs_ub - rhs) + name=slack_name, lowerbound=0, upperbound=lhs_ub - rhs + ) var_added = True else: raise QiskitOptimizationError( - 'The type of Sense in {} is not supported'.format(name)) + "The type of Sense in {} is not supported".format(name) + ) # Add a new equality constraint. new_linear = copy.deepcopy(linear) @@ -332,14 +357,21 @@ def _add_continuous_slack_var_quadratic_constraint(self, linear, quadratic, sens new_linear[slack_name] = sign self._dst.quadratic_constraint(new_linear, quadratic, "==", rhs, name) - def _add_auto_slack_var_quadratic_constraint(self, linear, quadratic, sense, rhs, name): + def _add_auto_slack_var_quadratic_constraint( + self, linear, quadratic, sense, rhs, name + ): # If a coefficient that is not integer exist, use a continuous slack variable - if (self._contains_any_float_value(list(linear.values())) - or self._contains_any_float_value(list(quadratic.values()))): - self._add_continuous_slack_var_quadratic_constraint(linear, quadratic, sense, rhs, name) + if self._contains_any_float_value( + list(linear.values()) + ) or self._contains_any_float_value(list(quadratic.values())): + self._add_continuous_slack_var_quadratic_constraint( + linear, quadratic, sense, rhs, name + ) # Else use an integer slack variable else: - self._add_integer_slack_var_quadratic_constraint(linear, quadratic, sense, rhs, name) + self._add_integer_slack_var_quadratic_constraint( + linear, quadratic, sense, rhs, name + ) def _calc_linear_bounds(self, linear): lhs_lb, lhs_ub = 0, 0 diff --git a/qiskit_optimization/converters/integer_to_binary.py b/qiskit_optimization/converters/integer_to_binary.py index bc78e27e7..42a12513e 100644 --- a/qiskit_optimization/converters/integer_to_binary.py +++ b/qiskit_optimization/converters/integer_to_binary.py @@ -46,7 +46,7 @@ class IntegerToBinary(QuadraticProgramConverter): Annealers. arxiv.org:1706.01945. """ - _delimiter = '@' # users are supposed not to use this character in variable names + _delimiter = "@" # users are supposed not to use this character in variable names def __init__(self) -> None: self._src = None # type: Optional[QuadraticProgram] @@ -101,17 +101,19 @@ def convert(self, problem: QuadraticProgram) -> QuadraticProgram: return self._dst def _convert_var( - self, name: str, lowerbound: float, upperbound: float + self, name: str, lowerbound: float, upperbound: float ) -> List[Tuple[str, int]]: var_range = upperbound - lowerbound power = int(np.log2(var_range)) bounded_coef = var_range - (2 ** power - 1) coeffs = [2 ** i for i in range(power)] + [bounded_coef] - return [(name + self._delimiter + str(i), coef) for i, coef in enumerate(coeffs)] + return [ + (name + self._delimiter + str(i), coef) for i, coef in enumerate(coeffs) + ] def _convert_linear_coefficients_dict( - self, coefficients: Dict[str, float] + self, coefficients: Dict[str, float] ) -> Tuple[Dict[str, float], float]: constant = 0.0 linear = {} # type: Dict[str, float] @@ -127,7 +129,7 @@ def _convert_linear_coefficients_dict( return linear, constant def _convert_quadratic_coefficients_dict( - self, coefficients: Dict[Tuple[str, str], float] + self, coefficients: Dict[Tuple[str, str], float] ) -> Tuple[Dict[Tuple[str, str], float], Dict[str, float], float]: constant = 0.0 linear = {} # type: Dict[str, float] @@ -185,7 +187,8 @@ def _substitute_int_var(self): # set linear constraints for constraint in self._src.linear_constraints: linear, constant = self._convert_linear_coefficients_dict( - constraint.linear.to_dict(use_name=True)) + constraint.linear.to_dict(use_name=True) + ) self._dst.linear_constraint( linear, constraint.sense, constraint.rhs - constant, constraint.name ) @@ -204,7 +207,11 @@ def _substitute_int_var(self): linear[i] = linear.get(i, 0) + v self._dst.quadratic_constraint( - linear, quadratic, constraint.sense, constraint.rhs - constant, constraint.name + linear, + quadratic, + constraint.sense, + constraint.rhs - constant, + constraint.name, ) def interpret(self, x: Union[np.ndarray, List[float]]) -> np.ndarray: @@ -222,7 +229,10 @@ def interpret(self, x: Union[np.ndarray, List[float]]) -> np.ndarray: new_x = np.zeros(self._src.get_num_vars()) for i, var in enumerate(self._src.variables): if var in self._conv: - new_x[i] = sum(sol[aux] * coef for aux, coef in self._conv[var]) + var.lowerbound + new_x[i] = ( + sum(sol[aux] * coef for aux, coef in self._conv[var]) + + var.lowerbound + ) else: new_x[i] = sol[var.name] return np.array(new_x) diff --git a/qiskit_optimization/converters/linear_equality_to_penalty.py b/qiskit_optimization/converters/linear_equality_to_penalty.py index 101e2ec52..1214c3fe0 100644 --- a/qiskit_optimization/converters/linear_equality_to_penalty.py +++ b/qiskit_optimization/converters/linear_equality_to_penalty.py @@ -75,7 +75,9 @@ def convert(self, problem: QuadraticProgram) -> QuadraticProgram: elif x.vartype == Variable.Type.INTEGER: self._dst.integer_var(x.lowerbound, x.upperbound, x.name) else: - raise QiskitOptimizationError('Unsupported vartype: {}'.format(x.vartype)) + raise QiskitOptimizationError( + "Unsupported vartype: {}".format(x.vartype) + ) # get original objective terms offset = self._src.objective.constant @@ -88,8 +90,8 @@ def convert(self, problem: QuadraticProgram) -> QuadraticProgram: if constraint.sense != Constraint.Sense.EQ: raise QiskitOptimizationError( - 'An inequality constraint exists. ' - 'The method supports only equality constraints.' + "An inequality constraint exists. " + "The method supports only equality constraints." ) constant = constraint.rhs @@ -114,7 +116,9 @@ def convert(self, problem: QuadraticProgram) -> QuadraticProgram: # according to implementation of quadratic terms in OptimizationModel, # don't need to multiply by 2, since loops run over (x, y) and (y, x). tup = cast(Union[Tuple[int, int], Tuple[str, str]], (j, k)) - quadratic[tup] = quadratic.get(tup, 0.0) + sense * penalty * coef_1 * coef_2 + quadratic[tup] = ( + quadratic.get(tup, 0.0) + sense * penalty * coef_1 * coef_2 + ) if self._src.objective.sense == QuadraticObjective.Sense.MINIMIZE: self._dst.minimize(offset, linear, quadratic) @@ -145,10 +149,10 @@ def _auto_define_penalty(self) -> float: terms.extend(coef for coef in constraint.linear.to_dict().values()) if any(isinstance(term, float) and not term.is_integer() for term in terms): logger.warning( - 'Warning: Using %f for the penalty coefficient because ' - 'a float coefficient exists in constraints. \n' - 'The value could be too small. ' - 'If so, set the penalty coefficient manually.', + "Warning: Using %f for the penalty coefficient because " + "a float coefficient exists in constraints. \n" + "The value could be too small. " + "If so, set the penalty coefficient manually.", default_penalty, ) return default_penalty @@ -157,9 +161,13 @@ def _auto_define_penalty(self) -> float: # Firstly, add 1 to guarantee that infeasible answers will be greater than upper bound. penalties = [1.0] # add linear terms of the object function. - penalties.extend(abs(coef) for coef in self._src.objective.linear.to_dict().values()) + penalties.extend( + abs(coef) for coef in self._src.objective.linear.to_dict().values() + ) # add quadratic terms of the object function. - penalties.extend(abs(coef) for coef in self._src.objective.quadratic.to_dict().values()) + penalties.extend( + abs(coef) for coef in self._src.objective.quadratic.to_dict().values() + ) return fsum(penalties) @@ -178,8 +186,8 @@ def interpret(self, x: Union[np.ndarray, List[float]]) -> np.ndarray: """ if len(x) != self._src.get_num_vars(): raise QiskitOptimizationError( - 'The number of variables in the passed result differs from ' - 'that of the original problem.' + "The number of variables in the passed result differs from " + "that of the original problem." ) return np.asarray(x) diff --git a/qiskit_optimization/converters/quadratic_program_converter.py b/qiskit_optimization/converters/quadratic_program_converter.py index 212e1caa0..c16cc23ee 100644 --- a/qiskit_optimization/converters/quadratic_program_converter.py +++ b/qiskit_optimization/converters/quadratic_program_converter.py @@ -34,5 +34,5 @@ def convert(self, problem: QuadraticProgram) -> QuadraticProgram: @abstractmethod def interpret(self, x: Union[np.ndarray, List[float]]) -> np.ndarray: - """ Interpret a result into another form using the information of conversion""" + """Interpret a result into another form using the information of conversion""" raise NotImplementedError diff --git a/qiskit_optimization/converters/quadratic_program_to_qubo.py b/qiskit_optimization/converters/quadratic_program_to_qubo.py index 95ee0c39c..60e01c9a7 100644 --- a/qiskit_optimization/converters/quadratic_program_to_qubo.py +++ b/qiskit_optimization/converters/quadratic_program_to_qubo.py @@ -24,13 +24,13 @@ class QuadraticProgramToQubo(QuadraticProgramConverter): """Convert a given optimization problem to a new problem that is a QUBO. - Examples: - >>> from qiskit_optimization.problems import QuadraticProgram - >>> from qiskit_optimization.converters import QuadraticProgramToQubo - >>> problem = QuadraticProgram() - >>> # define a problem - >>> conv = QuadraticProgramToQubo() - >>> problem2 = conv.convert(problem) + Examples: + >>> from qiskit_optimization.problems import QuadraticProgram + >>> from qiskit_optimization.converters import QuadraticProgramToQubo + >>> problem = QuadraticProgram() + >>> # define a problem + >>> conv = QuadraticProgramToQubo() + >>> problem2 = conv.convert(problem) """ def __init__(self, penalty: Optional[float] = None) -> None: @@ -45,7 +45,7 @@ def __init__(self, penalty: Optional[float] = None) -> None: from ..converters.linear_equality_to_penalty import LinearEqualityToPenalty self._int_to_bin = IntegerToBinary() - self._ineq_to_eq = InequalityToEquality(mode='integer') + self._ineq_to_eq = InequalityToEquality(mode="integer") self._penalize_lin_eq_constraints = LinearEqualityToPenalty(penalty=penalty) def convert(self, problem: QuadraticProgram) -> QuadraticProgram: @@ -64,7 +64,7 @@ def convert(self, problem: QuadraticProgram) -> QuadraticProgram: # analyze compatibility of problem msg = self.get_compatibility_msg(problem) if len(msg) > 0: - raise QiskitOptimizationError('Incompatible problem: {}'.format(msg)) + raise QiskitOptimizationError("Incompatible problem: {}".format(msg)) # Convert inequality constraints into equality constraints by adding slack variables problem_ = self._ineq_to_eq.convert(problem) @@ -81,11 +81,11 @@ def convert(self, problem: QuadraticProgram) -> QuadraticProgram: def interpret(self, x: Union[np.ndarray, List[float]]) -> np.ndarray: """Convert a result of a converted problem into that of the original problem. - Args: - x: The result of the converted problem. + Args: + x: The result of the converted problem. - Returns: - The result of the original problem. + Returns: + The result of the original problem. """ x = self._penalize_lin_eq_constraints.interpret(x) x = self._int_to_bin.interpret(x) @@ -107,33 +107,37 @@ def get_compatibility_msg(problem: QuadraticProgram) -> str: """ # initialize message - msg = '' + msg = "" # check whether there are incompatible variable types if problem.get_num_continuous_vars() > 0: - msg += 'Continuous variables are not supported! ' + msg += "Continuous variables are not supported! " # check whether there are incompatible constraint types if len(problem.quadratic_constraints) > 0: - msg += 'Quadratic constraints are not supported. ' + msg += "Quadratic constraints are not supported. " # check whether there are float coefficients in constraints compatible_with_integer_slack = True for l_constraint in problem.linear_constraints: linear = l_constraint.linear.to_dict() - if any(isinstance(coef, float) and not coef.is_integer() for coef in linear.values()): + if any( + isinstance(coef, float) and not coef.is_integer() + for coef in linear.values() + ): compatible_with_integer_slack = False for q_constraint in problem.quadratic_constraints: linear = q_constraint.linear.to_dict() quadratic = q_constraint.quadratic.to_dict() if any( - isinstance(coef, float) and not coef.is_integer() - for coef in quadratic.values() + isinstance(coef, float) and not coef.is_integer() + for coef in quadratic.values() ) or any( - isinstance(coef, float) and not coef.is_integer() for coef in linear.values() + isinstance(coef, float) and not coef.is_integer() + for coef in linear.values() ): compatible_with_integer_slack = False if not compatible_with_integer_slack: - msg += 'Can not convert inequality constraints to equality constraint because \ - float coefficients are in constraints. ' + msg += "Can not convert inequality constraints to equality constraint because \ + float coefficients are in constraints. " # if an error occurred, return error message, otherwise, return None return msg diff --git a/qiskit_optimization/exceptions.py b/qiskit_optimization/exceptions.py index a29d214e2..dc827da0f 100644 --- a/qiskit_optimization/exceptions.py +++ b/qiskit_optimization/exceptions.py @@ -17,4 +17,5 @@ class QiskitOptimizationError(QiskitError): """Class for errors returned by Qiskit's optimization module.""" + pass diff --git a/qiskit_optimization/infinity.py b/qiskit_optimization/infinity.py index 0907d8568..59d87a529 100644 --- a/qiskit_optimization/infinity.py +++ b/qiskit_optimization/infinity.py @@ -13,4 +13,4 @@ """Infinity constant.""" -INFINITY = 1.0E+20 # pylint: disable=invalid-name +INFINITY = 1.0e20 # pylint: disable=invalid-name diff --git a/qiskit_optimization/problems/__init__.py b/qiskit_optimization/problems/__init__.py index 0fc996aac..593500646 100644 --- a/qiskit_optimization/problems/__init__.py +++ b/qiskit_optimization/problems/__init__.py @@ -55,14 +55,15 @@ from .quadratic_program_element import QuadraticProgramElement from .variable import Variable, VarType -__all__ = ['Constraint', - 'LinearExpression', - 'LinearConstraint', - 'QuadraticExpression', - 'QuadraticConstraint', - 'QuadraticObjective', - 'QuadraticProgram', - 'QuadraticProgramElement', - 'Variable', - 'VarType' - ] +__all__ = [ + "Constraint", + "LinearExpression", + "LinearConstraint", + "QuadraticExpression", + "QuadraticConstraint", + "QuadraticObjective", + "QuadraticProgram", + "QuadraticProgramElement", + "Variable", + "VarType", +] diff --git a/qiskit_optimization/problems/constraint.py b/qiskit_optimization/problems/constraint.py index fe82dde9d..866aea222 100644 --- a/qiskit_optimization/problems/constraint.py +++ b/qiskit_optimization/problems/constraint.py @@ -31,7 +31,7 @@ class ConstraintSense(Enum): EQ = 2 @staticmethod - def convert(sense: Union[str, 'ConstraintSense']) -> 'ConstraintSense': + def convert(sense: Union[str, "ConstraintSense"]) -> "ConstraintSense": """Convert a string into a corresponding sense of constraints Args: @@ -46,11 +46,24 @@ def convert(sense: Union[str, 'ConstraintSense']) -> 'ConstraintSense': if isinstance(sense, ConstraintSense): return sense sense = sense.upper() - if sense not in ['E', 'L', 'G', 'EQ', 'LE', 'GE', '=', '==', '<=', '<', '>=', '>']: - raise QiskitOptimizationError('Invalid sense: {}'.format(sense)) - if sense in ['E', 'EQ', '=', '==']: + if sense not in [ + "E", + "L", + "G", + "EQ", + "LE", + "GE", + "=", + "==", + "<=", + "<", + ">=", + ">", + ]: + raise QiskitOptimizationError("Invalid sense: {}".format(sense)) + if sense in ["E", "EQ", "=", "=="]: return ConstraintSense.EQ - elif sense in ['L', 'LE', '<=', '<']: + elif sense in ["L", "LE", "<=", "<"]: return ConstraintSense.LE else: return ConstraintSense.GE @@ -61,9 +74,10 @@ class Constraint(QuadraticProgramElement): Sense = ConstraintSense - def __init__(self, quadratic_program: Any, name: str, sense: ConstraintSense, - rhs: float) -> None: - """ Initializes the constraint. + def __init__( + self, quadratic_program: Any, name: str, sense: ConstraintSense, rhs: float + ) -> None: + """Initializes the constraint. Args: quadratic_program: The parent QuadraticProgram. diff --git a/qiskit_optimization/problems/linear_constraint.py b/qiskit_optimization/problems/linear_constraint.py index 51e4b1793..367204121 100644 --- a/qiskit_optimization/problems/linear_constraint.py +++ b/qiskit_optimization/problems/linear_constraint.py @@ -22,17 +22,19 @@ class LinearConstraint(Constraint): - """ Representation of a linear constraint.""" + """Representation of a linear constraint.""" # Note: added, duplicating in effect that in Constraint, to avoid issues with Sphinx Sense = ConstraintSense - def __init__(self, - quadratic_program: Any, name: str, - linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]], - sense: ConstraintSense, - rhs: float - ) -> None: + def __init__( + self, + quadratic_program: Any, + name: str, + linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]], + sense: ConstraintSense, + rhs: float, + ) -> None: """ Args: quadratic_program: The parent quadratic program. @@ -54,8 +56,10 @@ def linear(self) -> LinearExpression: return self._linear @linear.setter - def linear(self, linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]]) \ - -> None: + def linear( + self, + linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]], + ) -> None: """Sets the linear expression corresponding to the left-hand-side of the constraint. The coefficients can either be given by an array, a (sparse) 1d matrix, a list or a dictionary. diff --git a/qiskit_optimization/problems/linear_expression.py b/qiskit_optimization/problems/linear_expression.py index 03af74956..d348e6bf6 100644 --- a/qiskit_optimization/problems/linear_expression.py +++ b/qiskit_optimization/problems/linear_expression.py @@ -22,11 +22,15 @@ class LinearExpression(QuadraticProgramElement): - """ Representation of a linear expression by its coefficients.""" - - def __init__(self, quadratic_program: Any, - coefficients: Union[ndarray, spmatrix, List[float], - Dict[Union[int, str], float]]) -> None: + """Representation of a linear expression by its coefficients.""" + + def __init__( + self, + quadratic_program: Any, + coefficients: Union[ + ndarray, spmatrix, List[float], Dict[Union[int, str], float] + ], + ) -> None: """Creates a new linear expression. The linear expression can be defined via an array, a list, a sparse matrix, or a dictionary @@ -59,10 +63,9 @@ def __setitem__(self, i: Union[int, str], value: float) -> None: i = self.quadratic_program.variables_index[i] self._coefficients[0, i] = value - def _coeffs_to_dok_matrix(self, - coefficients: Union[ndarray, spmatrix, - List, Dict[Union[int, str], float]] - ) -> dok_matrix: + def _coeffs_to_dok_matrix( + self, coefficients: Union[ndarray, spmatrix, List, Dict[Union[int, str], float]] + ) -> dok_matrix: """Maps given 1d-coefficients to a dok_matrix. Args: @@ -74,8 +77,11 @@ def _coeffs_to_dok_matrix(self, Raises: QiskitOptimizationError: if coefficients are given in unsupported format. """ - if isinstance(coefficients, list) or \ - isinstance(coefficients, ndarray) and len(coefficients.shape) == 1: + if ( + isinstance(coefficients, list) + or isinstance(coefficients, ndarray) + and len(coefficients.shape) == 1 + ): coefficients = dok_matrix([coefficients]) elif isinstance(coefficients, spmatrix): coefficients = dok_matrix(coefficients) @@ -92,7 +98,7 @@ def _coeffs_to_dok_matrix(self, @property def coefficients(self) -> dok_matrix: - """ Returns the coefficients of the linear expression. + """Returns the coefficients of the linear expression. Returns: The coefficients of the linear expression. @@ -100,10 +106,12 @@ def coefficients(self) -> dok_matrix: return self._coefficients @coefficients.setter - def coefficients(self, - coefficients: Union[ndarray, spmatrix, - List[float], Dict[Union[str, int], float]] - ) -> None: + def coefficients( + self, + coefficients: Union[ + ndarray, spmatrix, List[float], Dict[Union[str, int], float] + ], + ) -> None: """Sets the coefficients of the linear expression. Args: @@ -130,8 +138,10 @@ def to_dict(self, use_name: bool = False) -> Dict[Union[int, str], float]: An dictionary with the coefficients corresponding to the linear expression. """ if use_name: - return {self.quadratic_program.variables[k].name: v - for (_, k), v in self._coefficients.items()} + return { + self.quadratic_program.variables[k].name: v + for (_, k), v in self._coefficients.items() + } else: return {k: v for (_, k), v in self._coefficients.items()} @@ -154,7 +164,9 @@ def evaluate(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> flo return val # pylint: disable=unused-argument - def evaluate_gradient(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> ndarray: + def evaluate_gradient( + self, x: Union[ndarray, List, Dict[Union[int, str], float]] + ) -> ndarray: """Evaluate the gradient of the linear expression for given variables. Args: diff --git a/qiskit_optimization/problems/quadratic_constraint.py b/qiskit_optimization/problems/quadratic_constraint.py index 9e8433b93..636b8aea3 100644 --- a/qiskit_optimization/problems/quadratic_constraint.py +++ b/qiskit_optimization/problems/quadratic_constraint.py @@ -23,19 +23,25 @@ class QuadraticConstraint(Constraint): - """ Representation of a quadratic constraint.""" + """Representation of a quadratic constraint.""" # Note: added, duplicating in effect that in Constraint, to avoid issues with Sphinx Sense = ConstraintSense - def __init__(self, - quadratic_program: Any, name: str, - linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]], - quadratic: Union[ndarray, spmatrix, List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], float]], - sense: ConstraintSense, - rhs: float - ) -> None: + def __init__( + self, + quadratic_program: Any, + name: str, + linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]], + quadratic: Union[ + ndarray, + spmatrix, + List[List[float]], + Dict[Tuple[Union[int, str], Union[int, str]], float], + ], + sense: ConstraintSense, + rhs: float, + ) -> None: """Constructs a quadratic constraint, consisting of a linear and a quadratic term. Args: @@ -60,8 +66,10 @@ def linear(self) -> LinearExpression: return self._linear @linear.setter - def linear(self, linear: Union[ndarray, spmatrix, List[float], - Dict[Union[str, int], float]]) -> None: + def linear( + self, + linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]], + ) -> None: """Sets the linear expression corresponding to the left-hand-side of the constraint. The coefficients can either be given by an array, a (sparse) 1d matrix, a list or a dictionary. @@ -82,9 +90,15 @@ def quadratic(self) -> QuadraticExpression: return self._quadratic @quadratic.setter - def quadratic(self, quadratic: Union[ndarray, spmatrix, List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], float]]) \ - -> None: + def quadratic( + self, + quadratic: Union[ + ndarray, + spmatrix, + List[List[float]], + Dict[Tuple[Union[int, str], Union[int, str]], float], + ], + ) -> None: """Sets the quadratic expression corresponding to the left-hand-side of the constraint. The coefficients can either be given by an array, a (sparse) matrix, a list or a dictionary. diff --git a/qiskit_optimization/problems/quadratic_expression.py b/qiskit_optimization/problems/quadratic_expression.py index 07b324f61..5f4cb3c86 100644 --- a/qiskit_optimization/problems/quadratic_expression.py +++ b/qiskit_optimization/problems/quadratic_expression.py @@ -23,11 +23,18 @@ class QuadraticExpression(QuadraticProgramElement): - """ Representation of a quadratic expression by its coefficients.""" - - def __init__(self, quadratic_program: Any, - coefficients: Union[ndarray, spmatrix, List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], float]]) -> None: + """Representation of a quadratic expression by its coefficients.""" + + def __init__( + self, + quadratic_program: Any, + coefficients: Union[ + ndarray, + spmatrix, + List[List[float]], + Dict[Tuple[Union[int, str], Union[int, str]], float], + ], + ) -> None: """Creates a new quadratic expression. The quadratic expression can be defined via an array, a list, a sparse matrix, or a @@ -59,7 +66,9 @@ def __getitem__(self, key: Tuple[Union[int, str], Union[int, str]]) -> float: j = self.quadratic_program.variables_index[j] return self.coefficients[min(i, j), max(i, j)] - def __setitem__(self, key: Tuple[Union[int, str], Union[int, str]], value: float) -> None: + def __setitem__( + self, key: Tuple[Union[int, str], Union[int, str]], value: float + ) -> None: """Sets the coefficient where i, j can be a variable names or indices. Args: @@ -73,10 +82,15 @@ def __setitem__(self, key: Tuple[Union[int, str], Union[int, str]], value: float j = self.quadratic_program.variables_index[j] self.coefficients[min(i, j), max(i, j)] = value - def _coeffs_to_dok_matrix(self, - coefficients: Union[ndarray, spmatrix, List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], - float]]) -> dok_matrix: + def _coeffs_to_dok_matrix( + self, + coefficients: Union[ + ndarray, + spmatrix, + List[List[float]], + Dict[Tuple[Union[int, str], Union[int, str]], float], + ], + ) -> dok_matrix: """Maps given coefficients to a dok_matrix. Args: @@ -102,24 +116,25 @@ def _coeffs_to_dok_matrix(self, coefficients = coeffs else: raise QiskitOptimizationError( - "Unsupported format for coefficients: {}".format(coefficients)) + "Unsupported format for coefficients: {}".format(coefficients) + ) return self._triangle_matrix(coefficients) @staticmethod def _triangle_matrix(mat: dok_matrix) -> dok_matrix: - lower = tril(mat, -1, format='dok') + lower = tril(mat, -1, format="dok") # `todok` is necessary because subtraction results in other format return (mat + lower.transpose() - lower).todok() @staticmethod def _symmetric_matrix(mat: dok_matrix) -> dok_matrix: - upper = triu(mat, 1, format='dok') / 2 + upper = triu(mat, 1, format="dok") / 2 # `todok` is necessary because subtraction results in other format return (mat + upper.transpose() - upper).todok() @property def coefficients(self) -> dok_matrix: - """ Returns the coefficients of the quadratic expression. + """Returns the coefficients of the quadratic expression. Returns: The coefficients of the quadratic expression. @@ -127,10 +142,15 @@ def coefficients(self) -> dok_matrix: return self._coefficients @coefficients.setter - def coefficients(self, - coefficients: Union[ndarray, spmatrix, List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], float]] - ) -> None: + def coefficients( + self, + coefficients: Union[ + ndarray, + spmatrix, + List[List[float]], + Dict[Tuple[Union[int, str], Union[int, str]], float], + ], + ) -> None: """Sets the coefficients of the quadratic expression. Args: @@ -147,11 +167,16 @@ def to_array(self, symmetric: bool = False) -> ndarray: Returns: An array with the coefficients corresponding to the quadratic expression. """ - coeffs = self._symmetric_matrix(self._coefficients) if symmetric else self._coefficients + coeffs = ( + self._symmetric_matrix(self._coefficients) + if symmetric + else self._coefficients + ) return coeffs.toarray() - def to_dict(self, symmetric: bool = False, use_name: bool = False) \ - -> Dict[Union[Tuple[int, int], Tuple[str, str]], float]: + def to_dict( + self, symmetric: bool = False, use_name: bool = False + ) -> Dict[Union[Tuple[int, int], Tuple[str, str]], float]: """Returns the coefficients of the quadratic expression as dictionary, either using tuples of variable names or indices as keys. @@ -162,11 +187,19 @@ def to_dict(self, symmetric: bool = False, use_name: bool = False) \ Returns: An dictionary with the coefficients corresponding to the quadratic expression. """ - coeffs = self._symmetric_matrix(self._coefficients) if symmetric else self._coefficients + coeffs = ( + self._symmetric_matrix(self._coefficients) + if symmetric + else self._coefficients + ) if use_name: - return {(self.quadratic_program.variables[i].name, - self.quadratic_program.variables[j].name): v - for (i, j), v in coeffs.items()} + return { + ( + self.quadratic_program.variables[i].name, + self.quadratic_program.variables[j].name, + ): v + for (i, j), v in coeffs.items() + } else: return {(int(i), int(j)): v for (i, j), v in coeffs.items()} @@ -187,7 +220,9 @@ def evaluate(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> flo # return the result return val - def evaluate_gradient(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> ndarray: + def evaluate_gradient( + self, x: Union[ndarray, List, Dict[Union[int, str], float]] + ) -> ndarray: """Evaluate the gradient of the quadratic expression for given variables. Args: @@ -204,8 +239,9 @@ def evaluate_gradient(self, x: Union[ndarray, List, Dict[Union[int, str], float] # return the result return val - def _cast_as_array(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> \ - Union[dok_matrix, np.ndarray]: + def _cast_as_array( + self, x: Union[ndarray, List, Dict[Union[int, str], float]] + ) -> Union[dok_matrix, np.ndarray]: """Converts input to an array if it is a dictionary or list.""" if isinstance(x, dict): x_aux = np.zeros(self.quadratic_program.get_num_vars()) diff --git a/qiskit_optimization/problems/quadratic_objective.py b/qiskit_optimization/problems/quadratic_objective.py index a400c74ec..82c70f165 100644 --- a/qiskit_optimization/problems/quadratic_objective.py +++ b/qiskit_optimization/problems/quadratic_objective.py @@ -25,6 +25,7 @@ class ObjSense(Enum): """Objective Sense Type.""" + MINIMIZE = 1 MAXIMIZE = -1 @@ -36,13 +37,21 @@ class QuadraticObjective(QuadraticProgramElement): Sense = ObjSense - def __init__(self, quadratic_program: Any, - constant: float = 0.0, - linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]] = None, - quadratic: Union[ndarray, spmatrix, List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], float]] = None, - sense: ObjSense = ObjSense.MINIMIZE - ) -> None: + def __init__( + self, + quadratic_program: Any, + constant: float = 0.0, + linear: Union[ + ndarray, spmatrix, List[float], Dict[Union[str, int], float] + ] = None, + quadratic: Union[ + ndarray, + spmatrix, + List[List[float]], + Dict[Tuple[Union[int, str], Union[int, str]], float], + ] = None, + sense: ObjSense = ObjSense.MINIMIZE, + ) -> None: """Constructs a quadratic objective function. Args: @@ -90,8 +99,10 @@ def linear(self) -> LinearExpression: return self._linear @linear.setter - def linear(self, linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]] - ) -> None: + def linear( + self, + linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]], + ) -> None: """Sets the coefficients of the linear part of the objective function. Args: @@ -110,9 +121,15 @@ def quadratic(self) -> QuadraticExpression: return self._quadratic @quadratic.setter - def quadratic(self, quadratic: Union[ndarray, spmatrix, List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], float]] - ) -> None: + def quadratic( + self, + quadratic: Union[ + ndarray, + spmatrix, + List[List[float]], + Dict[Tuple[Union[int, str], Union[int, str]], float], + ], + ) -> None: """Sets the coefficients of the quadratic part of the objective function. Args: @@ -150,7 +167,9 @@ def evaluate(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> flo """ return self.constant + self.linear.evaluate(x) + self.quadratic.evaluate(x) - def evaluate_gradient(self, x: Union[ndarray, List, Dict[Union[int, str], float]]) -> ndarray: + def evaluate_gradient( + self, x: Union[ndarray, List, Dict[Union[int, str], float]] + ) -> ndarray: """Evaluate the gradient of the quadratic objective for given variable values. Args: diff --git a/qiskit_optimization/problems/quadratic_program.py b/qiskit_optimization/problems/quadratic_program.py index 0a60106d8..477e7b57b 100644 --- a/qiskit_optimization/problems/quadratic_program.py +++ b/qiskit_optimization/problems/quadratic_program.py @@ -57,6 +57,7 @@ class QuadraticProgramStatus(Enum): """Status of QuadraticProgram""" + VALID = 0 INFEASIBLE = 1 @@ -67,9 +68,10 @@ class QuadraticProgram: This representation supports inequality and equality constraints, as well as continuous, binary, and integer variables. """ + Status = QuadraticProgramStatus - def __init__(self, name: str = '') -> None: + def __init__(self, name: str = "") -> None: """ Args: name: The name of the quadratic program. @@ -95,7 +97,7 @@ def clear(self) -> None: """Clears the quadratic program, i.e., deletes all variables, constraints, the objective function as well as the name. """ - self._name = '' + self._name = "" self._status = QuadraticProgram.Status.VALID self._variables.clear() @@ -155,36 +157,47 @@ def variables_index(self) -> Dict[str, int]: """ return self._variables_index - def _add_variable(self, - lowerbound: Union[float, int], - upperbound: Union[float, int], - vartype: VarType, - name: Optional[str]) -> Variable: + def _add_variable( + self, + lowerbound: Union[float, int], + upperbound: Union[float, int], + vartype: VarType, + name: Optional[str], + ) -> Variable: if name is None: - name = 'x' - key_format = '{}' + name = "x" + key_format = "{}" else: - key_format = '' - return self._add_variables(1, lowerbound, upperbound, vartype, name, key_format)[1][0] - - def _add_variables(self, - keys: Union[int, Sequence], - lowerbound: Union[float, int], - upperbound: Union[float, int], - vartype: VarType, - name: Optional[str], - key_format: str) -> Tuple[List[str], List[Variable]]: + key_format = "" + return self._add_variables( + 1, lowerbound, upperbound, vartype, name, key_format + )[1][0] + + def _add_variables( + self, + keys: Union[int, Sequence], + lowerbound: Union[float, int], + upperbound: Union[float, int], + vartype: VarType, + name: Optional[str], + key_format: str, + ) -> Tuple[List[str], List[Variable]]: if isinstance(keys, int) and keys < 1: raise QiskitOptimizationError( - "Cannot create non-positive number of variables: {}".format(keys)) + "Cannot create non-positive number of variables: {}".format(keys) + ) if name is None: - name = 'x' - if '{{}}' in key_format: + name = "x" + if "{{}}" in key_format: raise QiskitOptimizationError( - "Formatter cannot contain nested substitutions: {}".format(key_format)) - if key_format.count('{}') > 1: + "Formatter cannot contain nested substitutions: {}".format(key_format) + ) + if key_format.count("{}") > 1: raise QiskitOptimizationError( - "Formatter cannot contain more than one substitution: {}".format(key_format)) + "Formatter cannot contain more than one substitution: {}".format( + key_format + ) + ) def _find_name(name, key_format, k): prev = None @@ -192,7 +205,8 @@ def _find_name(name, key_format, k): new_name = name + key_format.format(k) if new_name == prev: raise QiskitOptimizationError( - "Variable name already exists: {}".format(new_name)) + "Variable name already exists: {}".format(new_name) + ) if new_name in self._variables_index: k += 1 prev = new_name @@ -211,7 +225,8 @@ def _find_name(name, key_format, k): indexed_name, k = _find_name(name, key_format, k) if indexed_name in self._variables_index: raise QiskitOptimizationError( - "Variable name already exists: {}".format(indexed_name)) + "Variable name already exists: {}".format(indexed_name) + ) names.append(indexed_name) self._variables_index[indexed_name] = self.get_num_vars() variable = Variable(self, indexed_name, lowerbound, upperbound, vartype) @@ -219,13 +234,15 @@ def _find_name(name, key_format, k): variables.append(variable) return names, variables - def _var_dict(self, - keys: Union[int, Sequence], - lowerbound: Union[float, int], - upperbound: Union[float, int], - vartype: VarType, - name: Optional[str], - key_format: str) -> Dict[str, Variable]: + def _var_dict( + self, + keys: Union[int, Sequence], + lowerbound: Union[float, int], + upperbound: Union[float, int], + vartype: VarType, + name: Optional[str], + key_format: str, + ) -> Dict[str, Variable]: """ Adds a positive number of variables to the variable list and index and returns a dictionary mapping the variable names to their instances. If 'key_format' is present, @@ -252,15 +269,22 @@ def _var_dict(self, nested substitution. """ return dict( - zip(*self._add_variables(keys, lowerbound, upperbound, vartype, name, key_format))) - - def _var_list(self, - keys: Union[int, Sequence], - lowerbound: Union[float, int], - upperbound: Union[float, int], - vartype: VarType, - name: Optional[str], - key_format: str) -> List[Variable]: + zip( + *self._add_variables( + keys, lowerbound, upperbound, vartype, name, key_format + ) + ) + ) + + def _var_list( + self, + keys: Union[int, Sequence], + lowerbound: Union[float, int], + upperbound: Union[float, int], + vartype: VarType, + name: Optional[str], + key_format: str, + ) -> List[Variable]: """ Adds a positive number of variables to the variable list and index and returns a list of variable instances. @@ -284,12 +308,16 @@ def _var_list(self, QiskitOptimizationError: if `key_format` has more than one substitution or a nested substitution. """ - return self._add_variables(keys, lowerbound, upperbound, vartype, name, key_format)[1] - - def continuous_var(self, - lowerbound: Union[float, int] = 0, - upperbound: Union[float, int] = INFINITY, - name: Optional[str] = None) -> Variable: + return self._add_variables( + keys, lowerbound, upperbound, vartype, name, key_format + )[1] + + def continuous_var( + self, + lowerbound: Union[float, int] = 0, + upperbound: Union[float, int] = INFINITY, + name: Optional[str] = None, + ) -> Variable: """Adds a continuous variable to the quadratic program. Args: @@ -303,14 +331,18 @@ def continuous_var(self, Raises: QiskitOptimizationError: if the variable name is already occupied. """ - return self._add_variable(lowerbound, upperbound, Variable.Type.CONTINUOUS, name) - - def continuous_var_dict(self, - keys: Union[int, Sequence], - lowerbound: Union[float, int] = 0, - upperbound: Union[float, int] = INFINITY, - name: Optional[str] = None, - key_format: str = '{}') -> Dict[str, Variable]: + return self._add_variable( + lowerbound, upperbound, Variable.Type.CONTINUOUS, name + ) + + def continuous_var_dict( + self, + keys: Union[int, Sequence], + lowerbound: Union[float, int] = 0, + upperbound: Union[float, int] = INFINITY, + name: Optional[str] = None, + key_format: str = "{}", + ) -> Dict[str, Variable]: """ Uses 'var_dict' to construct a dictionary of continuous variables @@ -332,15 +364,18 @@ def continuous_var_dict(self, QiskitOptimizationError: if `key_format` has more than one substitution or a nested substitution. """ - return self._var_dict(keys, lowerbound, upperbound, Variable.Type.CONTINUOUS, name, - key_format) - - def continuous_var_list(self, - keys: Union[int, Sequence], - lowerbound: Union[float, int] = 0, - upperbound: Union[float, int] = INFINITY, - name: Optional[str] = None, - key_format: str = '{}') -> List[Variable]: + return self._var_dict( + keys, lowerbound, upperbound, Variable.Type.CONTINUOUS, name, key_format + ) + + def continuous_var_list( + self, + keys: Union[int, Sequence], + lowerbound: Union[float, int] = 0, + upperbound: Union[float, int] = INFINITY, + name: Optional[str] = None, + key_format: str = "{}", + ) -> List[Variable]: """ Uses 'var_list' to construct a list of continuous variables @@ -362,8 +397,9 @@ def continuous_var_list(self, QiskitOptimizationError: if `key_format` has more than one substitution or a nested substitution. """ - return self._var_list(keys, lowerbound, upperbound, Variable.Type.CONTINUOUS, - name, key_format) + return self._var_list( + keys, lowerbound, upperbound, Variable.Type.CONTINUOUS, name, key_format + ) def binary_var(self, name: Optional[str] = None) -> Variable: """Adds a binary variable to the quadratic program. @@ -379,10 +415,12 @@ def binary_var(self, name: Optional[str] = None) -> Variable: """ return self._add_variable(0, 1, Variable.Type.BINARY, name) - def binary_var_dict(self, - keys: Union[int, Sequence], - name: Optional[str] = None, - key_format: str = '{}') -> Dict[str, Variable]: + def binary_var_dict( + self, + keys: Union[int, Sequence], + name: Optional[str] = None, + key_format: str = "{}", + ) -> Dict[str, Variable]: """ Uses 'var_dict' to construct a dictionary of binary variables @@ -404,10 +442,12 @@ def binary_var_dict(self, """ return self._var_dict(keys, 0, 1, Variable.Type.BINARY, name, key_format) - def binary_var_list(self, - keys: Union[int, Sequence], - name: Optional[str] = None, - key_format: str = '{}') -> List[Variable]: + def binary_var_list( + self, + keys: Union[int, Sequence], + name: Optional[str] = None, + key_format: str = "{}", + ) -> List[Variable]: """ Uses 'var_list' to construct a list of binary variables @@ -429,10 +469,12 @@ def binary_var_list(self, """ return self._var_list(keys, 0, 1, Variable.Type.BINARY, name, key_format) - def integer_var(self, - lowerbound: Union[float, int] = 0, - upperbound: Union[float, int] = INFINITY, - name: Optional[str] = None) -> Variable: + def integer_var( + self, + lowerbound: Union[float, int] = 0, + upperbound: Union[float, int] = INFINITY, + name: Optional[str] = None, + ) -> Variable: """Adds an integer variable to the quadratic program. Args: @@ -448,12 +490,14 @@ def integer_var(self, """ return self._add_variable(lowerbound, upperbound, Variable.Type.INTEGER, name) - def integer_var_dict(self, - keys: Union[int, Sequence], - lowerbound: Union[float, int] = 0, - upperbound: Union[float, int] = INFINITY, - name: Optional[str] = None, - key_format: str = '{}') -> Dict[str, Variable]: + def integer_var_dict( + self, + keys: Union[int, Sequence], + lowerbound: Union[float, int] = 0, + upperbound: Union[float, int] = INFINITY, + name: Optional[str] = None, + key_format: str = "{}", + ) -> Dict[str, Variable]: """ Uses 'var_dict' to construct a dictionary of integer variables @@ -475,14 +519,18 @@ def integer_var_dict(self, QiskitOptimizationError: if `key_format` has more than one substitution or a nested substitution. """ - return self._var_dict(keys, lowerbound, upperbound, Variable.Type.INTEGER, name, key_format) - - def integer_var_list(self, - keys: Union[int, Sequence], - lowerbound: Union[float, int] = 0, - upperbound: Union[float, int] = INFINITY, - name: Optional[str] = None, - key_format: str = '{}') -> List[Variable]: + return self._var_dict( + keys, lowerbound, upperbound, Variable.Type.INTEGER, name, key_format + ) + + def integer_var_list( + self, + keys: Union[int, Sequence], + lowerbound: Union[float, int] = 0, + upperbound: Union[float, int] = INFINITY, + name: Optional[str] = None, + key_format: str = "{}", + ) -> List[Variable]: """ Uses 'var_list' to construct a dictionary of integer variables @@ -504,7 +552,9 @@ def integer_var_list(self, QiskitOptimizationError: if `key_format` has more than one substitution or a nested substitution. """ - return self._var_list(keys, lowerbound, upperbound, Variable.Type.INTEGER, name, key_format) + return self._var_list( + keys, lowerbound, upperbound, Variable.Type.INTEGER, name, key_format + ) def get_variable(self, i: Union[int, str]) -> Variable: """Returns a variable for a given name or index. @@ -576,11 +626,15 @@ def linear_constraints_index(self) -> Dict[str, int]: """ return self._linear_constraints_index - def linear_constraint(self, - linear: Union[ndarray, spmatrix, List[float], - Dict[Union[int, str], float]] = None, - sense: Union[str, ConstraintSense] = '<=', - rhs: float = 0.0, name: Optional[str] = None) -> LinearConstraint: + def linear_constraint( + self, + linear: Union[ + ndarray, spmatrix, List[float], Dict[Union[int, str], float] + ] = None, + sense: Union[str, ConstraintSense] = "<=", + rhs: float = 0.0, + name: Optional[str] = None, + ) -> LinearConstraint: """Adds a linear equality constraint to the quadratic program of the form: linear * x sense rhs. @@ -603,16 +657,19 @@ def linear_constraint(self, if name: if name in self.linear_constraints_index: raise QiskitOptimizationError( - "Linear constraint's name already exists: {}".format(name)) + "Linear constraint's name already exists: {}".format(name) + ) else: k = self.get_num_linear_constraints() - while 'c{}'.format(k) in self.linear_constraints_index: + while "c{}".format(k) in self.linear_constraints_index: k += 1 - name = 'c{}'.format(k) + name = "c{}".format(k) self.linear_constraints_index[name] = len(self.linear_constraints) if linear is None: linear = {} - constraint = LinearConstraint(self, name, linear, Constraint.Sense.convert(sense), rhs) + constraint = LinearConstraint( + self, name, linear, Constraint.Sense.convert(sense), rhs + ) self.linear_constraints.append(constraint) return constraint @@ -660,14 +717,21 @@ def quadratic_constraints_index(self) -> Dict[str, int]: """ return self._quadratic_constraints_index - def quadratic_constraint(self, - linear: Union[ndarray, spmatrix, List[float], - Dict[Union[int, str], float]] = None, - quadratic: Union[ndarray, spmatrix, List[List[float]], - Dict[Tuple[Union[int, str], - Union[int, str]], float]] = None, - sense: Union[str, ConstraintSense] = '<=', - rhs: float = 0.0, name: Optional[str] = None) -> QuadraticConstraint: + def quadratic_constraint( + self, + linear: Union[ + ndarray, spmatrix, List[float], Dict[Union[int, str], float] + ] = None, + quadratic: Union[ + ndarray, + spmatrix, + List[List[float]], + Dict[Tuple[Union[int, str], Union[int, str]], float], + ] = None, + sense: Union[str, ConstraintSense] = "<=", + rhs: float = 0.0, + name: Optional[str] = None, + ) -> QuadraticConstraint: """Adds a quadratic equality constraint to the quadratic program of the form: x * Q * x <= rhs. @@ -690,19 +754,21 @@ def quadratic_constraint(self, if name: if name in self.quadratic_constraints_index: raise QiskitOptimizationError( - "Quadratic constraint name already exists: {}".format(name)) + "Quadratic constraint name already exists: {}".format(name) + ) else: k = self.get_num_quadratic_constraints() - while 'q{}'.format(k) in self.quadratic_constraints_index: + while "q{}".format(k) in self.quadratic_constraints_index: k += 1 - name = 'q{}'.format(k) + name = "q{}".format(k) self.quadratic_constraints_index[name] = len(self.quadratic_constraints) if linear is None: linear = {} if quadratic is None: quadratic = {} - constraint = QuadraticConstraint(self, name, linear, quadratic, - Constraint.Sense.convert(sense), rhs) + constraint = QuadraticConstraint( + self, name, linear, quadratic, Constraint.Sense.convert(sense), rhs + ) self.quadratic_constraints.append(constraint) return constraint @@ -745,8 +811,9 @@ def remove_linear_constraint(self, i: Union[str, int]) -> None: if isinstance(i, str): i = self._linear_constraints_index[i] del self._linear_constraints[i] - self._linear_constraints_index = {cst.name: j for j, cst in - enumerate(self._linear_constraints)} + self._linear_constraints_index = { + cst.name: j for j, cst in enumerate(self._linear_constraints) + } def remove_quadratic_constraint(self, i: Union[str, int]) -> None: """Remove a quadratic constraint @@ -761,8 +828,9 @@ def remove_quadratic_constraint(self, i: Union[str, int]) -> None: if isinstance(i, str): i = self._quadratic_constraints_index[i] del self._quadratic_constraints[i] - self._quadratic_constraints_index = {cst.name: j for j, cst in - enumerate(self._quadratic_constraints)} + self._quadratic_constraints_index = { + cst.name: j for j, cst in enumerate(self._quadratic_constraints) + } @property def objective(self) -> QuadraticObjective: @@ -773,12 +841,19 @@ def objective(self) -> QuadraticObjective: """ return self._objective - def minimize(self, - constant: float = 0.0, - linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]] = None, - quadratic: Union[ndarray, spmatrix, List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], float]] = None - ) -> None: + def minimize( + self, + constant: float = 0.0, + linear: Union[ + ndarray, spmatrix, List[float], Dict[Union[str, int], float] + ] = None, + quadratic: Union[ + ndarray, + spmatrix, + List[List[float]], + Dict[Tuple[Union[int, str], Union[int, str]], float], + ] = None, + ) -> None: """Sets a quadratic objective to be minimized. Args: @@ -789,15 +864,23 @@ def minimize(self, Returns: The created quadratic objective. """ - self._objective = QuadraticObjective(self, constant, linear, quadratic, - QuadraticObjective.Sense.MINIMIZE) - - def maximize(self, - constant: float = 0.0, - linear: Union[ndarray, spmatrix, List[float], Dict[Union[str, int], float]] = None, - quadratic: Union[ndarray, spmatrix, List[List[float]], - Dict[Tuple[Union[int, str], Union[int, str]], float]] = None - ) -> None: + self._objective = QuadraticObjective( + self, constant, linear, quadratic, QuadraticObjective.Sense.MINIMIZE + ) + + def maximize( + self, + constant: float = 0.0, + linear: Union[ + ndarray, spmatrix, List[float], Dict[Union[str, int], float] + ] = None, + quadratic: Union[ + ndarray, + spmatrix, + List[List[float]], + Dict[Tuple[Union[int, str], Union[int, str]], float], + ] = None, + ) -> None: """Sets a quadratic objective to be maximized. Args: @@ -808,8 +891,9 @@ def maximize(self, Returns: The created quadratic objective. """ - self._objective = QuadraticObjective(self, constant, linear, quadratic, - QuadraticObjective.Sense.MAXIMIZE) + self._objective = QuadraticObjective( + self, constant, linear, quadratic, QuadraticObjective.Sense.MAXIMIZE + ) def from_docplex(self, model: Model) -> None: """Loads this quadratic program from a docplex model. @@ -844,7 +928,8 @@ def from_docplex(self, model: Model) -> None: x_new = self.integer_var(x.lb, x.ub, x.name) else: raise QiskitOptimizationError( - "Unsupported variable type: {} {}".format(x.name, x.vartype)) + "Unsupported variable type: {} {}".format(x.name, x.vartype) + ) var_names[x] = x_new.name # objective sense @@ -883,13 +968,15 @@ def from_docplex(self, model: Model) -> None: if isinstance(constraint, DocplexQuadraticConstraint): # ignore quadratic constraints here and process them later continue - if not isinstance(constraint, DocplexLinearConstraint) or \ - isinstance(constraint, NotEqualConstraint): + if not isinstance(constraint, DocplexLinearConstraint) or isinstance( + constraint, NotEqualConstraint + ): # If any constraint is not linear/quadratic constraints, it raises an error. # Notice that NotEqualConstraint is a subclass of Docplex's LinearConstraint, # but it cannot be handled by optimization. raise QiskitOptimizationError( - 'Unsupported constraint: {}'.format(constraint)) + "Unsupported constraint: {}".format(constraint) + ) name = constraint.name sense = constraint.sense @@ -911,14 +998,15 @@ def from_docplex(self, model: Model) -> None: lhs[var_names[x]] = lhs.get(var_names[x], 0.0) - right_expr.get_coef(x) if sense == sense.EQ: - self.linear_constraint(lhs, '==', rhs, name) + self.linear_constraint(lhs, "==", rhs, name) elif sense == sense.GE: - self.linear_constraint(lhs, '>=', rhs, name) + self.linear_constraint(lhs, ">=", rhs, name) elif sense == sense.LE: - self.linear_constraint(lhs, '<=', rhs, name) + self.linear_constraint(lhs, "<=", rhs, name) else: raise QiskitOptimizationError( - "Unsupported constraint sense: {}".format(constraint)) + "Unsupported constraint sense: {}".format(constraint) + ) # get quadratic constraints for constraint in model.iter_quadratic_constraints(): @@ -946,8 +1034,9 @@ def from_docplex(self, model: Model) -> None: if right_expr.is_quad_expr(): for x in right_expr.linear_part.iter_variables(): - linear[var_names[x]] = linear.get(var_names[x], 0.0) - \ - right_expr.linear_part.get_coef(x) + linear[var_names[x]] = linear.get( + var_names[x], 0.0 + ) - right_expr.linear_part.get_coef(x) for quad_triplet in right_expr.iter_quad_triplets(): i = var_names[quad_triplet[0]] j = var_names[quad_triplet[1]] @@ -955,17 +1044,20 @@ def from_docplex(self, model: Model) -> None: quadratic[i, j] = quadratic.get((i, j), 0.0) - v else: for x in right_expr.iter_variables(): - linear[var_names[x]] = linear.get(var_names[x], 0.0) - right_expr.get_coef(x) + linear[var_names[x]] = linear.get( + var_names[x], 0.0 + ) - right_expr.get_coef(x) if sense == sense.EQ: - self.quadratic_constraint(linear, quadratic, '==', rhs, name) + self.quadratic_constraint(linear, quadratic, "==", rhs, name) elif sense == sense.GE: - self.quadratic_constraint(linear, quadratic, '>=', rhs, name) + self.quadratic_constraint(linear, quadratic, ">=", rhs, name) elif sense == sense.LE: - self.quadratic_constraint(linear, quadratic, '<=', rhs, name) + self.quadratic_constraint(linear, quadratic, "<=", rhs, name) else: raise QiskitOptimizationError( - "Unsupported constraint sense: {}".format(constraint)) + "Unsupported constraint sense: {}".format(constraint) + ) def to_docplex(self) -> Model: """Returns a docplex model corresponding to this quadratic program. @@ -984,14 +1076,20 @@ def to_docplex(self) -> Model: var = {} for idx, x in enumerate(self.variables): if x.vartype == Variable.Type.CONTINUOUS: - var[idx] = mdl.continuous_var(lb=x.lowerbound, ub=x.upperbound, name=x.name) + var[idx] = mdl.continuous_var( + lb=x.lowerbound, ub=x.upperbound, name=x.name + ) elif x.vartype == Variable.Type.BINARY: var[idx] = mdl.binary_var(name=x.name) elif x.vartype == Variable.Type.INTEGER: - var[idx] = mdl.integer_var(lb=x.lowerbound, ub=x.upperbound, name=x.name) + var[idx] = mdl.integer_var( + lb=x.lowerbound, ub=x.upperbound, name=x.name + ) else: # should never happen - raise QiskitOptimizationError('Unsupported variable type: {}'.format(x.vartype)) + raise QiskitOptimizationError( + "Unsupported variable type: {}".format(x.vartype) + ) # add objective objective = self.objective.constant @@ -1022,15 +1120,19 @@ def to_docplex(self) -> Model: mdl.add_constraint(linear_expr <= rhs, ctname=name) else: # should never happen - raise QiskitOptimizationError("Unsupported constraint sense: {}".format(sense)) + raise QiskitOptimizationError( + "Unsupported constraint sense: {}".format(sense) + ) # add quadratic constraints for i, q_constraint in enumerate(self.quadratic_constraints): name = q_constraint.name rhs = q_constraint.rhs - if rhs == 0 \ - and q_constraint.linear.coefficients.nnz == 0 \ - and q_constraint.quadratic.coefficients.nnz == 0: + if ( + rhs == 0 + and q_constraint.linear.coefficients.nnz == 0 + and q_constraint.quadratic.coefficients.nnz == 0 + ): continue quadratic_expr = 0 for j, v in q_constraint.linear.to_dict().items(): @@ -1046,7 +1148,9 @@ def to_docplex(self) -> Model: mdl.add_constraint(quadratic_expr <= rhs, ctname=name) else: # should never happen - raise QiskitOptimizationError("Unsupported constraint sense: {}".format(sense)) + raise QiskitOptimizationError( + "Unsupported constraint sense: {}".format(sense) + ) return mdl @@ -1063,10 +1167,13 @@ def pprint_as_string(self) -> str: Returns: A string representing the quadratic program. """ - warnings.warn("The pprint_as_string method is deprecated and will be " - "removed in a future release. Instead use the" - "to_docplex() method and run pprint_as_string() on that " - "output", DeprecationWarning) + warnings.warn( + "The pprint_as_string method is deprecated and will be " + "removed in a future release. Instead use the" + "to_docplex() method and run pprint_as_string() on that " + "output", + DeprecationWarning, + ) return self.to_docplex().pprint_as_string() def prettyprint(self, out: Optional[str] = None) -> None: @@ -1076,10 +1183,13 @@ def prettyprint(self, out: Optional[str] = None) -> None: out: The output stream or file name to print to. if you specify a file name, the output file name is has '.mod' as suffix. """ - warnings.warn("The prettyprint method is deprecated and will be " - "removed in a future release. Instead use the" - "to_docplex() method and run prettyprint() on that " - "output", DeprecationWarning) + warnings.warn( + "The prettyprint method is deprecated and will be " + "removed in a future release. Instead use the" + "to_docplex() method and run prettyprint() on that " + "output", + DeprecationWarning, + ) self.to_docplex().prettyprint(out) def read_from_lp_file(self, filename: str) -> None: @@ -1099,21 +1209,22 @@ def read_from_lp_file(self, filename: str) -> None: import cplex # pylint: disable=unused-import except ImportError as ex: raise MissingOptionalLibraryError( - libname='CPLEX', - name='QuadraticProgram.read_from_lp_file', - pip_install="pip install 'qiskit-optimization[cplex]'") from ex + libname="CPLEX", + name="QuadraticProgram.read_from_lp_file", + pip_install="pip install 'qiskit-optimization[cplex]'", + ) from ex def _parse_problem_name(filename: str) -> str: # Because docplex model reader uses the base name as model name, # we parse the model name in the LP file manually. # https://ibmdecisionoptimization.github.io/docplex-doc/mp/docplex.mp.model_reader.html - prefix = '\\Problem name:' - model_name = '' + prefix = "\\Problem name:" + model_name = "" with open(filename) as file: for line in file: if line.startswith(prefix): - model_name = line[len(prefix):].strip() - if not line.startswith('\\'): + model_name = line[len(prefix) :].strip() + if not line.startswith("\\"): break return model_name @@ -1136,9 +1247,12 @@ def write_to_lp_file(self, filename: str) -> None: self.to_docplex().export_as_lp(filename) def substitute_variables( - self, constants: Optional[Dict[Union[str, int], float]] = None, - variables: Optional[Dict[Union[str, int], Tuple[Union[str, int], float]]] = None) \ - -> 'QuadraticProgram': + self, + constants: Optional[Dict[Union[str, int], float]] = None, + variables: Optional[ + Dict[Union[str, int], Tuple[Union[str, int], float]] + ] = None, + ) -> "QuadraticProgram": """Substitutes variables with constants or other variables. Args: @@ -1176,28 +1290,31 @@ def to_ising(self) -> Tuple[OperatorBase, float]: """ # if problem has variables that are not binary, raise an error if self.get_num_vars() > self.get_num_binary_vars(): - raise QiskitOptimizationError('The type of variable must be a binary variable. ' - 'Use a QuadraticProgramToQubo converter to convert ' - 'integer variables to binary variables. ' - 'If the problem contains continuous variables, ' - 'currently we can not apply VQE/QAOA directly. ' - 'you might want to use an ADMM optimizer ' - 'for the problem. ') + raise QiskitOptimizationError( + "The type of variable must be a binary variable. " + "Use a QuadraticProgramToQubo converter to convert " + "integer variables to binary variables. " + "If the problem contains continuous variables, " + "currently we can not apply VQE/QAOA directly. " + "you might want to use an ADMM optimizer " + "for the problem. " + ) # if constraints exist, raise an error - if self.linear_constraints \ - or self.quadratic_constraints: - raise QiskitOptimizationError('An constraint exists. ' - 'The method supports only model with no constraints. ' - 'Use a QuadraticProgramToQubo converter. ' - 'It converts inequality constraints to equality ' - 'constraints, and then, it converters equality ' - 'constraints to penalty terms of the object function.') + if self.linear_constraints or self.quadratic_constraints: + raise QiskitOptimizationError( + "An constraint exists. " + "The method supports only model with no constraints. " + "Use a QuadraticProgramToQubo converter. " + "It converts inequality constraints to equality " + "constraints, and then, it converters equality " + "constraints to penalty terms of the object function." + ) # initialize Hamiltonian. num_nodes = self.get_num_vars() pauli_list = [] - offset = 0. + offset = 0.0 zero = zeros(num_nodes, dtype=bool) # set a sign corresponding to a maximized or minimized problem. @@ -1260,9 +1377,12 @@ def to_ising(self) -> Tuple[OperatorBase, float]: return qubit_op, offset - def from_ising(self, - qubit_op: Union[OperatorBase, PauliSumOp], - offset: float = 0.0, linear: bool = False) -> None: + def from_ising( + self, + qubit_op: Union[OperatorBase, PauliSumOp], + offset: float = 0.0, + linear: bool = False, + ) -> None: r"""Create a quadratic program from a qubit operator and a shift value. Args: @@ -1285,13 +1405,13 @@ def from_ising(self, # pylint: disable=unidiomatic-typecheck if type(qubit_op) == ListOp: raise NotImplementedError( - 'Conversion of a ListOp is not supported, convert each ' - 'operator in the ListOp separately.' + "Conversion of a ListOp is not supported, convert each " + "operator in the ListOp separately." ) # add binary variables for i in range(qubit_op.num_qubits): - self.binary_var(name='x_{0}'.format(i)) + self.binary_var(name="x_{0}".format(i)) # Create a QUBO matrix # The Qubo matrix is an upper triangular matrix. @@ -1320,14 +1440,18 @@ def from_ising(self, qubo_matrix[z_index[0], z_index[1]] = coeff.real else: raise QiskitOptimizationError( - 'There are more than 2 Pauli Zs in the Pauli term {}'.format(pauli.z) + "There are more than 2 Pauli Zs in the Pauli term {}".format( + pauli.z + ) ) # If there are Pauli Xs in the Pauli term, raise an error lst_x = pauli.x.tolist() x_index = [i for i, x in enumerate(lst_x) if x is True] if len(x_index) > 0: - raise QiskitOptimizationError('Pauli Xs exist in the Pauli {}'.format(pauli.x)) + raise QiskitOptimizationError( + "Pauli Xs exist in the Pauli {}".format(pauli.x) + ) # Initialize dicts for linear terms and quadratic terms linear_terms = {} @@ -1375,8 +1499,9 @@ def from_ising(self, self.minimize(constant=offset, linear=linear_terms, quadratic=quadratic_terms) offset -= offset - def get_feasibility_info(self, x: Union[List[float], np.ndarray]) \ - -> Tuple[bool, List[Variable], List[Constraint]]: + def get_feasibility_info( + self, x: Union[List[float], np.ndarray] + ) -> Tuple[bool, List[Variable], List[Constraint]]: """Returns whether a solution is feasible or not along with the violations. Args: x: a solution value, such as returned in an optimizer result. @@ -1391,8 +1516,9 @@ def get_feasibility_info(self, x: Union[List[float], np.ndarray]) \ # if input `x` is not the same len as the total vars, raise an error if len(x) != self.get_num_vars(): raise QiskitOptimizationError( - 'The size of solution `x`: {}, does not match the number of problem variables: ' - '{}'.format(len(x), self.get_num_vars())) + "The size of solution `x`: {}, does not match the number of problem variables: " + "{}".format(len(x), self.get_num_vars()) + ) # check whether the input satisfy the bounds of the problem violated_variables = [] # type: List[Variable] @@ -1403,14 +1529,17 @@ def get_feasibility_info(self, x: Union[List[float], np.ndarray]) \ # check whether the input satisfy the constraints of the problem violated_constraints = [] # type: List[Constraint] - for constraint in cast(List[Constraint], self._linear_constraints) + \ - cast(List[Constraint], self._quadratic_constraints): + for constraint in cast(List[Constraint], self._linear_constraints) + cast( + List[Constraint], self._quadratic_constraints + ): lhs = constraint.evaluate(x) if constraint.sense == ConstraintSense.LE and lhs > constraint.rhs: violated_constraints.append(constraint) elif constraint.sense == ConstraintSense.GE and lhs < constraint.rhs: violated_constraints.append(constraint) - elif constraint.sense == ConstraintSense.EQ and not isclose(lhs, constraint.rhs): + elif constraint.sense == ConstraintSense.EQ and not isclose( + lhs, constraint.rhs + ): violated_constraints.append(constraint) feasible = not violated_variables and not violated_constraints @@ -1436,7 +1565,7 @@ class SubstituteVariables: """A class to substitute variables of an optimization problem with constants for other variables""" - CONST = '__CONSTANT__' + CONST = "__CONSTANT__" def __init__(self): self._src = None # type: Optional[QuadraticProgram] @@ -1444,10 +1573,13 @@ def __init__(self): self._subs = {} # type: Dict[Union[int, str], Tuple[str, float]] def substitute_variables( - self, src: QuadraticProgram, - constants: Optional[Dict[Union[str, int], float]] = None, - variables: Optional[Dict[Union[str, int], Tuple[Union[str, int], float]]] = None) \ - -> QuadraticProgram: + self, + src: QuadraticProgram, + constants: Optional[Dict[Union[str, int], float]] = None, + variables: Optional[ + Dict[Union[str, int], Tuple[Union[str, int], float]] + ] = None, + ) -> QuadraticProgram: """Substitutes variables with constants or other variables. Args: @@ -1488,7 +1620,7 @@ def substitute_variables( @staticmethod def _feasible(sense: ConstraintSense, rhs: float) -> bool: """Checks feasibility of the following condition - 0 `sense` rhs + 0 `sense` rhs """ # I use the following pylint option because `rhs` should come to right # pylint: disable=misplaced-comparison-constant @@ -1521,27 +1653,38 @@ def _subs_dict(self, constants, variables): i_2 = self._src.get_variable(i).name if i_2 in subs: raise QiskitOptimizationError( - 'Cannot substitute the same variable twice: {} <- {}'.format(i, v)) + "Cannot substitute the same variable twice: {} <- {}".format( + i, v + ) + ) subs[i_2] = (self.CONST, v) if variables is not None: for i, (j, v) in variables.items(): if v == 0: raise QiskitOptimizationError( - 'coefficient must be non-zero: {} {} {}'.format(i, j, v)) + "coefficient must be non-zero: {} {} {}".format(i, j, v) + ) # substitute i <- j * v i_2 = self._src.get_variable(i).name j_2 = self._src.get_variable(j).name if i_2 == j_2: raise QiskitOptimizationError( - 'Cannot substitute the same variable: {} <- {} {}'.format(i, j, v)) + "Cannot substitute the same variable: {} <- {} {}".format( + i, j, v + ) + ) if i_2 in subs: raise QiskitOptimizationError( - 'Cannot substitute the same variable twice: {} <- {} {}'.format(i, j, v)) + "Cannot substitute the same variable twice: {} <- {} {}".format( + i, j, v + ) + ) if j_2 in subs: raise QiskitOptimizationError( - 'Cannot substitute by variable that gets substituted itself: ' - '{} <- {} {}'.format(i, j, v)) + "Cannot substitute by variable that gets substituted itself: " + "{} <- {} {}".format(i, j, v) + ) subs[i_2] = (j_2, v) self._subs = subs @@ -1562,8 +1705,7 @@ def _variables(self) -> bool: ub_i = self._src.get_variable(i).upperbound if j == self.CONST: if not lb_i <= v <= ub_i: - logger.warning( - 'Infeasible substitution for variable: %s', i) + logger.warning("Infeasible substitution for variable: %s", i) feasible = False else: # substitute i <- j * v @@ -1571,8 +1713,9 @@ def _variables(self) -> bool: # ub_i / v <= j <= lb_i / v if v < 0 if v == 0: raise QiskitOptimizationError( - 'Coefficient of variable substitution should be nonzero: ' - '{} {} {}'.format(i, j, v)) + "Coefficient of variable substitution should be nonzero: " + "{} {} {}".format(i, j, v) + ) if abs(lb_i) < INFINITY: new_lb_i = lb_i / v else: @@ -1594,14 +1737,18 @@ def _variables(self) -> bool: for var in self._dst.variables: if var.lowerbound > var.upperbound: logger.warning( - 'Infeasible lower and upper bound: %s %f %f', var, var.lowerbound, - var.upperbound) + "Infeasible lower and upper bound: %s %f %f", + var, + var.lowerbound, + var.upperbound, + ) feasible = False return feasible - def _linear_expression(self, lin_expr: LinearExpression) \ - -> Tuple[List[float], LinearExpression]: + def _linear_expression( + self, lin_expr: LinearExpression + ) -> Tuple[List[float], LinearExpression]: const = [] lin_dict = defaultdict(float) # type: Dict[Union[int, str], float] for i, w_i in lin_expr.to_dict(use_name=True).items(): @@ -1612,15 +1759,19 @@ def _linear_expression(self, lin_expr: LinearExpression) \ else: k = repl_i[0] lin_dict[k] += prod - new_lin = LinearExpression(quadratic_program=self._dst, - coefficients=lin_dict if lin_dict else {}) + new_lin = LinearExpression( + quadratic_program=self._dst, coefficients=lin_dict if lin_dict else {} + ) return const, new_lin - def _quadratic_expression(self, quad_expr: QuadraticExpression) \ - -> Tuple[List[float], Optional[LinearExpression], Optional[QuadraticExpression]]: + def _quadratic_expression( + self, quad_expr: QuadraticExpression + ) -> Tuple[List[float], Optional[LinearExpression], Optional[QuadraticExpression]]: const = [] lin_dict = defaultdict(float) # type: Dict[Union[int, str], float] - quad_dict = defaultdict(float) # type: Dict[Tuple[Union[int, str], Union[int, str]], float] + quad_dict = defaultdict( + float + ) # type: Dict[Tuple[Union[int, str], Union[int, str]], float] for (i, j), w_ij in quad_expr.to_dict(use_name=True).items(): repl_i = self._subs[i] if i in self._subs else (i, 1) repl_j = self._subs[j] if j in self._subs else (j, 1) @@ -1632,10 +1783,12 @@ def _quadratic_expression(self, quad_expr: QuadraticExpression) \ lin_dict[idx[0]] += prod else: const.append(prod) - new_lin = LinearExpression(quadratic_program=self._dst, - coefficients=lin_dict if lin_dict else {}) - new_quad = QuadraticExpression(quadratic_program=self._dst, - coefficients=quad_dict if quad_dict else {}) + new_lin = LinearExpression( + quadratic_program=self._dst, coefficients=lin_dict if lin_dict else {} + ) + new_quad = QuadraticExpression( + quadratic_program=self._dst, coefficients=quad_dict if quad_dict else {} + ) return const, new_lin, new_quad def _objective(self) -> bool: @@ -1646,9 +1799,13 @@ def _objective(self) -> bool: constant = fsum([obj.constant] + const1 + const2) linear = lin1.coefficients + lin2.coefficients if obj.sense == obj.sense.MINIMIZE: - self._dst.minimize(constant=constant, linear=linear, quadratic=quadratic.coefficients) + self._dst.minimize( + constant=constant, linear=linear, quadratic=quadratic.coefficients + ) else: - self._dst.maximize(constant=constant, linear=linear, quadratic=quadratic.coefficients) + self._dst.maximize( + constant=constant, linear=linear, quadratic=quadratic.coefficients + ) return True def _linear_constraints(self) -> bool: @@ -1657,11 +1814,17 @@ def _linear_constraints(self) -> bool: constant, linear = self._linear_expression(lin_cst.linear) rhs = -fsum([-lin_cst.rhs] + constant) if linear.coefficients.nnz > 0: - self._dst.linear_constraint(name=lin_cst.name, linear=linear.coefficients, - sense=lin_cst.sense, rhs=rhs) + self._dst.linear_constraint( + name=lin_cst.name, + linear=linear.coefficients, + sense=lin_cst.sense, + rhs=rhs, + ) else: if not self._feasible(lin_cst.sense, rhs): - logger.warning('constraint %s is infeasible due to substitution', lin_cst.name) + logger.warning( + "constraint %s is infeasible due to substitution", lin_cst.name + ) feasible = False return feasible @@ -1674,18 +1837,26 @@ def _quadratic_constraints(self) -> bool: linear = lin1.coefficients + lin2.coefficients if quadratic.coefficients.nnz > 0: - self._dst.quadratic_constraint(name=quad_cst.name, linear=linear, - quadratic=quadratic.coefficients, - sense=quad_cst.sense, rhs=rhs) + self._dst.quadratic_constraint( + name=quad_cst.name, + linear=linear, + quadratic=quadratic.coefficients, + sense=quad_cst.sense, + rhs=rhs, + ) elif linear.nnz > 0: name = quad_cst.name lin_names = set(lin.name for lin in self._dst.linear_constraints) while name in lin_names: - name = '_' + name - self._dst.linear_constraint(name=name, linear=linear, sense=quad_cst.sense, rhs=rhs) + name = "_" + name + self._dst.linear_constraint( + name=name, linear=linear, sense=quad_cst.sense, rhs=rhs + ) else: if not self._feasible(quad_cst.sense, rhs): - logger.warning('constraint %s is infeasible due to substitution', quad_cst.name) + logger.warning( + "constraint %s is infeasible due to substitution", quad_cst.name + ) feasible = False return feasible diff --git a/qiskit_optimization/problems/quadratic_program_element.py b/qiskit_optimization/problems/quadratic_program_element.py index 570ddb276..a9dac85ad 100644 --- a/qiskit_optimization/problems/quadratic_program_element.py +++ b/qiskit_optimization/problems/quadratic_program_element.py @@ -19,7 +19,7 @@ class QuadraticProgramElement: """Interface class for all objects that have a parent QuadraticProgram.""" def __init__(self, quadratic_program: Any) -> None: - """ Initialize object with parent QuadraticProgram. + """Initialize object with parent QuadraticProgram. Args: quadratic_program: The parent QuadraticProgram. @@ -29,7 +29,7 @@ def __init__(self, quadratic_program: Any) -> None: from .quadratic_program import QuadraticProgram if not isinstance(quadratic_program, QuadraticProgram): - raise TypeError('QuadraticProgram instance expected') + raise TypeError("QuadraticProgram instance expected") self._quadratic_program = quadratic_program @@ -55,6 +55,6 @@ def quadratic_program(self, quadratic_program: Any) -> None: from .quadratic_program import QuadraticProgram if not isinstance(quadratic_program, QuadraticProgram): - raise TypeError('QuadraticProgram instance expected') + raise TypeError("QuadraticProgram instance expected") self._quadratic_program = quadratic_program diff --git a/qiskit_optimization/problems/variable.py b/qiskit_optimization/problems/variable.py index 5a935f5bc..f7d351a69 100644 --- a/qiskit_optimization/problems/variable.py +++ b/qiskit_optimization/problems/variable.py @@ -22,6 +22,7 @@ class VarType(Enum): """Constants defining variable type.""" + CONTINUOUS = 0 BINARY = 1 INTEGER = 2 @@ -32,10 +33,14 @@ class Variable(QuadraticProgramElement): Type = VarType - def __init__(self, quadratic_program: Any, name: str, - lowerbound: Union[float, int] = 0, - upperbound: Union[float, int] = INFINITY, - vartype: VarType = VarType.CONTINUOUS) -> None: + def __init__( + self, + quadratic_program: Any, + name: str, + lowerbound: Union[float, int] = 0, + upperbound: Union[float, int] = INFINITY, + vartype: VarType = VarType.CONTINUOUS, + ) -> None: """Creates a new Variable. The variables is exposed by the top-level `QuadraticProgram` class @@ -136,7 +141,7 @@ def vartype(self, vartype: VarType) -> None: self._vartype = vartype def as_tuple(self) -> Tuple[str, Union[float, int], Union[float, int], VarType]: - """ Returns a tuple corresponding to this variable. + """Returns a tuple corresponding to this variable. Returns: A tuple corresponding to this variable consisting of name, lowerbound, upperbound and diff --git a/qiskit_optimization/version.py b/qiskit_optimization/version.py index 81a74005f..d0798f14f 100644 --- a/qiskit_optimization/version.py +++ b/qiskit_optimization/version.py @@ -22,21 +22,28 @@ def _minimal_ext_cmd(cmd): # construct minimal environment env = {} - for k in ['SYSTEMROOT', 'PATH']: + for k in ["SYSTEMROOT", "PATH"]: v = os.environ.get(k) if v is not None: env[k] = v # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - with subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, env=env, - cwd=os.path.join(os.path.dirname(QISKIT_DIR))) as proc: + env["LANGUAGE"] = "C" + env["LANG"] = "C" + env["LC_ALL"] = "C" + with subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env, + cwd=os.path.join(os.path.dirname(QISKIT_DIR)), + ) as proc: stdout, stderr = proc.communicate() if proc.returncode > 0: - raise OSError('Command {} exited with code {}: {}'.format( - cmd, proc.returncode, stderr.strip().decode('ascii'))) + raise OSError( + "Command {} exited with code {}: {}".format( + cmd, proc.returncode, stderr.strip().decode("ascii") + ) + ) return stdout @@ -44,8 +51,8 @@ def git_version(): """Get the current git head sha1.""" # Determine if we're at main try: - out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) - git_revision = out.strip().decode('ascii') + out = _minimal_ext_cmd(["git", "rev-parse", "HEAD"]) + git_revision = out.strip().decode("ascii") except OSError: git_revision = "Unknown" @@ -63,15 +70,15 @@ def get_version_info(): # up the build under Python 3. full_version = VERSION - if not os.path.exists(os.path.join(os.path.dirname(QISKIT_DIR), '.git')): + if not os.path.exists(os.path.join(os.path.dirname(QISKIT_DIR), ".git")): return full_version try: - release = _minimal_ext_cmd(['git', 'tag', '-l', '--points-at', 'HEAD']) + release = _minimal_ext_cmd(["git", "tag", "-l", "--points-at", "HEAD"]) except Exception: # pylint: disable=broad-except return full_version git_revision = git_version() if not release: - full_version += '.dev0+' + git_revision[:7] + full_version += ".dev0+" + git_revision[:7] return full_version diff --git a/requirements-dev.txt b/requirements-dev.txt index f3bda7ad3..e9a4c196f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,6 +1,6 @@ coverage>=4.4.0 matplotlib>=2.1 -pycodestyle +black==21.4b2 pylint>=2.7.1 pylintfileheader>=0.0.2 pylatexenc>=1.4 diff --git a/test/__init__.py b/test/__init__.py index e03279d2c..3d2502a61 100755 --- a/test/__init__.py +++ b/test/__init__.py @@ -12,10 +12,6 @@ """ Optimization test packages """ -from .optimization_test_case import (QiskitOptimizationTestCase, - requires_extra_library) +from .optimization_test_case import QiskitOptimizationTestCase, requires_extra_library -__all__ = [ - 'QiskitOptimizationTestCase', - 'requires_extra_library' -] +__all__ = ["QiskitOptimizationTestCase", "requires_extra_library"] diff --git a/test/algorithms/test_admm.py b/test/algorithms/test_admm.py index bc41f8148..145a52750 100644 --- a/test/algorithms/test_admm.py +++ b/test/algorithms/test_admm.py @@ -16,8 +16,12 @@ import numpy as np from docplex.mp.model import Model from qiskit_optimization.algorithms import CobylaOptimizer -from qiskit_optimization.algorithms.admm_optimizer import ADMMOptimizer, ADMMParameters, \ - ADMMOptimizationResult, ADMMState +from qiskit_optimization.algorithms.admm_optimizer import ( + ADMMOptimizer, + ADMMParameters, + ADMMOptimizationResult, + ADMMState, +) from qiskit_optimization.problems import QuadraticProgram @@ -26,16 +30,18 @@ class TestADMMOptimizer(QiskitOptimizationTestCase): def test_admm_maximization(self): """Tests a simple maximization problem using ADMM optimizer""" - mdl = Model('simple-max') - c = mdl.continuous_var(lb=0, ub=10, name='c') - x = mdl.binary_var(name='x') + mdl = Model("simple-max") + c = mdl.continuous_var(lb=0, ub=10, name="c") + x = mdl.binary_var(name="x") mdl.maximize(c + x * x) op = QuadraticProgram() op.from_docplex(mdl) admm_params = ADMMParameters() - solver = ADMMOptimizer(params=admm_params, continuous_optimizer=CobylaOptimizer()) + solver = ADMMOptimizer( + params=admm_params, continuous_optimizer=CobylaOptimizer() + ) solution = solver.solve(op) self.assertIsNotNone(solution) self.assertIsInstance(solution, ADMMOptimizationResult) @@ -58,12 +64,12 @@ def test_admm_ex4(self): Multi-block ADMM Heuristics for Mixed-Binary Optimization on Classical and Quantum Computers. arXiv preprint arXiv:2001.02069.""" - mdl = Model('ex4') + mdl = Model("ex4") - v = mdl.binary_var(name='v') - w = mdl.binary_var(name='w') + v = mdl.binary_var(name="v") + w = mdl.binary_var(name="w") # pylint:disable=invalid-name - t = mdl.binary_var(name='t') + t = mdl.binary_var(name="t") b = 2 @@ -75,8 +81,7 @@ def test_admm_ex4(self): op.from_docplex(mdl) admm_params = ADMMParameters( - rho_initial=1001, beta=1000, factor_c=900, - maxiter=100, three_block=False + rho_initial=1001, beta=1000, factor_c=900, maxiter=100, three_block=False ) solver = ADMMOptimizer(params=admm_params) @@ -84,9 +89,9 @@ def test_admm_ex4(self): self.assertIsNotNone(solution) self.assertIsInstance(solution, ADMMOptimizationResult) self.assertIsNotNone(solution.x) - np.testing.assert_almost_equal([1., 0., 1.], solution.x, 3) + np.testing.assert_almost_equal([1.0, 0.0, 1.0], solution.x, 3) self.assertIsNotNone(solution.fval) - np.testing.assert_almost_equal(2., solution.fval, 3) + np.testing.assert_almost_equal(2.0, solution.fval, 3) self.assertIsNotNone(solution.state) self.assertIsInstance(solution.state, ADMMState) @@ -96,12 +101,12 @@ def test_admm_ex4_no_bin_var_in_objective(self): is omitted in objective to test a problem when a binary variable defined but is used only in constraints. """ - mdl = Model('ex4') + mdl = Model("ex4") - v = mdl.binary_var(name='v') - w = mdl.binary_var(name='w') + v = mdl.binary_var(name="v") + w = mdl.binary_var(name="w") # pylint:disable=invalid-name - t = mdl.binary_var(name='t') + t = mdl.binary_var(name="t") b = 2 @@ -113,8 +118,7 @@ def test_admm_ex4_no_bin_var_in_objective(self): op.from_docplex(mdl) admm_params = ADMMParameters( - rho_initial=1001, beta=1000, factor_c=900, - maxiter=100, three_block=False + rho_initial=1001, beta=1000, factor_c=900, maxiter=100, three_block=False ) solver = ADMMOptimizer(params=admm_params) @@ -122,9 +126,9 @@ def test_admm_ex4_no_bin_var_in_objective(self): self.assertIsNotNone(solution) self.assertIsInstance(solution, ADMMOptimizationResult) self.assertIsNotNone(solution.x) - np.testing.assert_almost_equal([1., 0., 1.], solution.x, 3) + np.testing.assert_almost_equal([1.0, 0.0, 1.0], solution.x, 3) self.assertIsNotNone(solution.fval) - np.testing.assert_almost_equal(2., solution.fval, 3) + np.testing.assert_almost_equal(2.0, solution.fval, 3) self.assertIsNotNone(solution.state) self.assertIsInstance(solution.state, ADMMState) @@ -134,12 +138,12 @@ def test_admm_ex5(self): Multi-block ADMM Heuristics for Mixed-Binary Optimization on Classical and Quantum Computers. arXiv preprint arXiv:2001.02069.""" - mdl = Model('ex5') + 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') + 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") @@ -150,8 +154,7 @@ def test_admm_ex5(self): op.from_docplex(mdl) admm_params = ADMMParameters( - rho_initial=1001, beta=1000, factor_c=900, - maxiter=100, three_block=False + rho_initial=1001, beta=1000, factor_c=900, maxiter=100, three_block=False ) solver = ADMMOptimizer(params=admm_params) @@ -160,20 +163,20 @@ def test_admm_ex5(self): self.assertIsNotNone(solution) self.assertIsInstance(solution, ADMMOptimizationResult) self.assertIsNotNone(solution.x) - np.testing.assert_almost_equal([1., 0., 1.], solution.x, 3) + np.testing.assert_almost_equal([1.0, 0.0, 1.0], solution.x, 3) self.assertIsNotNone(solution.fval) - np.testing.assert_almost_equal(2., solution.fval, 3) + np.testing.assert_almost_equal(2.0, solution.fval, 3) 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') + 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') + 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") @@ -184,8 +187,12 @@ def test_admm_ex5_warm_start(self): op.from_docplex(mdl) admm_params = ADMMParameters( - rho_initial=1001, beta=1000, factor_c=900, - maxiter=100, three_block=False, warm_start=True + rho_initial=1001, + beta=1000, + factor_c=900, + maxiter=100, + three_block=False, + warm_start=True, ) solver = ADMMOptimizer(params=admm_params) @@ -194,9 +201,9 @@ def test_admm_ex5_warm_start(self): self.assertIsNotNone(solution) self.assertIsInstance(solution, ADMMOptimizationResult) self.assertIsNotNone(solution.x) - np.testing.assert_almost_equal([0., 1., 0.], solution.x, 3) + np.testing.assert_almost_equal([0.0, 1.0, 0.0], solution.x, 3) self.assertIsNotNone(solution.fval) - np.testing.assert_almost_equal(1., solution.fval, 3) + np.testing.assert_almost_equal(1.0, solution.fval, 3) self.assertIsNotNone(solution.state) self.assertIsInstance(solution.state, ADMMState) @@ -206,13 +213,13 @@ def test_admm_ex6(self): Multi-block ADMM Heuristics for Mixed-Binary Optimization on Classical and Quantum Computers. arXiv preprint arXiv:2001.02069.""" - mdl = Model('ex6') + mdl = Model("ex6") # pylint:disable=invalid-name - v = mdl.binary_var(name='v') - w = mdl.binary_var(name='w') - t = mdl.binary_var(name='t') - u = mdl.continuous_var(name='u') + v = mdl.binary_var(name="v") + w = mdl.binary_var(name="w") + t = mdl.binary_var(name="t") + u = mdl.continuous_var(name="u") mdl.minimize(v + w + t + 5 * (u - 2) ** 2) mdl.add_constraint(v + 2 * w + t + u <= 3, "cons1") @@ -223,8 +230,12 @@ def test_admm_ex6(self): op.from_docplex(mdl) admm_params = ADMMParameters( - rho_initial=1001, beta=1000, factor_c=900, - maxiter=100, three_block=True, tol=1.e-6 + rho_initial=1001, + beta=1000, + factor_c=900, + maxiter=100, + three_block=True, + tol=1.0e-6, ) solver = ADMMOptimizer(params=admm_params) @@ -233,24 +244,24 @@ def test_admm_ex6(self): self.assertIsNotNone(solution) self.assertIsInstance(solution, ADMMOptimizationResult) self.assertIsNotNone(solution.x) - np.testing.assert_almost_equal([1., 0., 0., 2.], solution.x, 3) + np.testing.assert_almost_equal([1.0, 0.0, 0.0, 2.0], solution.x, 3) self.assertIsNotNone(solution.fval) - np.testing.assert_almost_equal(1., solution.fval, 3) + np.testing.assert_almost_equal(1.0, solution.fval, 3) self.assertIsNotNone(solution.state) self.assertIsInstance(solution.state, ADMMState) def test_admm_ex6_max(self): """Example 6 as maximization""" - mdl = Model('ex6-max') + mdl = Model("ex6-max") # pylint:disable=invalid-name - v = mdl.binary_var(name='v') - w = mdl.binary_var(name='w') - t = mdl.binary_var(name='t') - u = mdl.continuous_var(name='u') + v = mdl.binary_var(name="v") + w = mdl.binary_var(name="w") + t = mdl.binary_var(name="t") + u = mdl.continuous_var(name="u") # mdl.minimize(v + w + t + 5 * (u - 2) ** 2) - mdl.maximize(- v - w - t - 5 * (u - 2) ** 2) + mdl.maximize(-v - w - t - 5 * (u - 2) ** 2) mdl.add_constraint(v + 2 * w + t + u <= 3, "cons1") mdl.add_constraint(v + w + t >= 1, "cons2") mdl.add_constraint(v + w == 1, "cons3") @@ -259,8 +270,12 @@ def test_admm_ex6_max(self): op.from_docplex(mdl) admm_params = ADMMParameters( - rho_initial=1001, beta=1000, factor_c=900, - maxiter=100, three_block=True, tol=1.e-6 + rho_initial=1001, + beta=1000, + factor_c=900, + maxiter=100, + three_block=True, + tol=1.0e-6, ) solver = ADMMOptimizer(params=admm_params) @@ -269,9 +284,9 @@ def test_admm_ex6_max(self): self.assertIsNotNone(solution) self.assertIsInstance(solution, ADMMOptimizationResult) self.assertIsNotNone(solution.x) - np.testing.assert_almost_equal([1., 0., 0., 2.], solution.x, 3) + np.testing.assert_almost_equal([1.0, 0.0, 0.0, 2.0], solution.x, 3) self.assertIsNotNone(solution.fval) - np.testing.assert_almost_equal(-1., solution.fval, 3) + np.testing.assert_almost_equal(-1.0, solution.fval, 3) self.assertIsNotNone(solution.state) self.assertIsInstance(solution.state, ADMMState) @@ -280,9 +295,9 @@ def test_equality_constraints_with_continuous_variables(self): mdl = Model("eq-constraints-cts-vars") # pylint:disable=invalid-name - v = mdl.binary_var(name='v') - w = mdl.continuous_var(name='w', lb=0.) - t = mdl.continuous_var(name='t', lb=0.) + v = mdl.binary_var(name="v") + w = mdl.continuous_var(name="w", lb=0.0) + t = mdl.continuous_var(name="t", lb=0.0) mdl.minimize(v + w + t) mdl.add_constraint(2 * v + w >= 2, "cons1") @@ -292,8 +307,11 @@ def test_equality_constraints_with_continuous_variables(self): op.from_docplex(mdl) admm_params = ADMMParameters( - rho_initial=1001, beta=1000, factor_c=900, - maxiter=100, three_block=True, + rho_initial=1001, + beta=1000, + factor_c=900, + maxiter=100, + three_block=True, ) solver = ADMMOptimizer(params=admm_params) @@ -302,18 +320,18 @@ def test_equality_constraints_with_continuous_variables(self): self.assertIsNotNone(solution) self.assertIsInstance(solution, ADMMOptimizationResult) self.assertIsNotNone(solution.x) - np.testing.assert_almost_equal([0., 1., 0.], solution.x, 3) + np.testing.assert_almost_equal([0.0, 1.0, 0.0], solution.x, 3) self.assertIsNotNone(solution.fval) - np.testing.assert_almost_equal(1., solution.fval, 3) + np.testing.assert_almost_equal(1.0, solution.fval, 3) self.assertIsNotNone(solution.state) self.assertIsInstance(solution.state, ADMMState) def test_quad_constraints(self): """Simple example to test quadratic constraints.""" - mdl = Model('quad-constraints') + mdl = Model("quad-constraints") - v = mdl.binary_var(name='v') - w = mdl.continuous_var(name='w', lb=0.) + v = mdl.binary_var(name="v") + w = mdl.continuous_var(name="w", lb=0.0) mdl.minimize(v + w) mdl.add_constraint(v + w >= 1, "cons2") @@ -323,8 +341,11 @@ def test_quad_constraints(self): op.from_docplex(mdl) admm_params = ADMMParameters( - rho_initial=1001, beta=1000, factor_c=900, - maxiter=100, three_block=True, + rho_initial=1001, + beta=1000, + factor_c=900, + maxiter=100, + three_block=True, ) solver = ADMMOptimizer(params=admm_params) @@ -333,9 +354,9 @@ def test_quad_constraints(self): self.assertIsNotNone(solution) self.assertIsInstance(solution, ADMMOptimizationResult) self.assertIsNotNone(solution.x) - np.testing.assert_almost_equal([0., 1.], solution.x, 3) + np.testing.assert_almost_equal([0.0, 1.0], solution.x, 3) self.assertIsNotNone(solution.fval) - np.testing.assert_almost_equal(1., solution.fval, 3) + np.testing.assert_almost_equal(1.0, solution.fval, 3) self.assertIsNotNone(solution.state) self.assertIsInstance(solution.state, ADMMState) @@ -353,10 +374,10 @@ def test_admm_setters_getters(self): def test_integer_variables(self): """Tests ADMM with integer variables.""" - mdl = Model('integer-variables') + mdl = Model("integer-variables") - v = mdl.integer_var(lb=5, ub=20, name='v') - w = mdl.continuous_var(name='w', lb=0.) + v = mdl.integer_var(lb=5, ub=20, name="v") + w = mdl.continuous_var(name="w", lb=0.0) mdl.minimize(v + w) op = QuadraticProgram() @@ -368,8 +389,8 @@ def test_integer_variables(self): self.assertIsNotNone(solution) self.assertIsInstance(solution, ADMMOptimizationResult) self.assertIsNotNone(solution.x) - np.testing.assert_almost_equal([5., 0.], solution.x, 3) + np.testing.assert_almost_equal([5.0, 0.0], solution.x, 3) self.assertIsNotNone(solution.fval) - np.testing.assert_almost_equal(5., solution.fval, 3) + np.testing.assert_almost_equal(5.0, solution.fval, 3) self.assertIsNotNone(solution.state) self.assertIsInstance(solution.state, ADMMState) diff --git a/test/algorithms/test_cobyla_optimizer.py b/test/algorithms/test_cobyla_optimizer.py index efac8f350..500af61e9 100644 --- a/test/algorithms/test_cobyla_optimizer.py +++ b/test/algorithms/test_cobyla_optimizer.py @@ -23,16 +23,16 @@ class TestCobylaOptimizer(QiskitOptimizationTestCase): - """Cobyla Optimizer Tests. """ + """Cobyla Optimizer Tests.""" def test_cobyla_optimizer(self): - """ Cobyla Optimizer Test. """ + """Cobyla Optimizer Test.""" # load optimization problem problem = QuadraticProgram() problem.continuous_var(upperbound=4) problem.continuous_var(upperbound=4) - problem.linear_constraint(linear=[1, 1], sense='=', rhs=2) + problem.linear_constraint(linear=[1, 1], sense="=", rhs=2) problem.minimize(linear=[2, 2], quadratic=[[2, 0.25], [0.25, 0.5]]) # solve problem with cobyla @@ -43,7 +43,7 @@ def test_cobyla_optimizer(self): self.assertAlmostEqual(result.fval, 5.8750) def test_cobyla_optimizer_with_quadratic_constraint(self): - """ Cobyla Optimizer Test With Quadratic Constraints. """ + """Cobyla Optimizer Test With Quadratic Constraints.""" # load optimization problem problem = QuadraticProgram() problem.continuous_var(upperbound=1) @@ -53,7 +53,7 @@ def test_cobyla_optimizer_with_quadratic_constraint(self): linear = [-1, -1] quadratic = [[1, 0], [0, 1]] - problem.quadratic_constraint(linear=linear, quadratic=quadratic, rhs=-1/2) + problem.quadratic_constraint(linear=linear, quadratic=quadratic, rhs=-1 / 2) # solve problem with cobyla cobyla = CobylaOptimizer() @@ -63,7 +63,7 @@ def test_cobyla_optimizer_with_quadratic_constraint(self): self.assertAlmostEqual(result.fval, 1.0, places=2) def test_cobyla_optimizer_with_variable_bounds(self): - """ Cobyla Optimizer Test With Variable Bounds. """ + """Cobyla Optimizer Test With Variable Bounds.""" # initialize optimizer cobyla = CobylaOptimizer() @@ -96,13 +96,13 @@ def test_cobyla_optimizer_with_variable_bounds(self): self.assertAlmostEqual(result.x[1], 2.0, places=6) def test_cobyla_optimizer_with_trials(self): - """ Cobyla Optimizer Test. """ + """Cobyla Optimizer Test.""" # load optimization problem problem = QuadraticProgram() problem.continuous_var(upperbound=4) problem.continuous_var(upperbound=4) - problem.linear_constraint(linear=[1, 1], sense='=', rhs=2) + problem.linear_constraint(linear=[1, 1], sense="=", rhs=2) problem.minimize(linear=[2, 2], quadratic=[[2, 0.25], [0.25, 0.5]]) # solve problem with cobyla @@ -113,5 +113,5 @@ def test_cobyla_optimizer_with_trials(self): self.assertAlmostEqual(result.fval, 5.8750) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/algorithms/test_cplex_optimizer.py b/test/algorithms/test_cplex_optimizer.py index 07fddca9a..f52fd6b6f 100644 --- a/test/algorithms/test_cplex_optimizer.py +++ b/test/algorithms/test_cplex_optimizer.py @@ -13,7 +13,10 @@ """ Test Cplex Optimizer """ import unittest -from test.optimization_test_case import QiskitOptimizationTestCase, requires_extra_library +from test.optimization_test_case import ( + QiskitOptimizationTestCase, + requires_extra_library, +) from ddt import ddt, data from qiskit_optimization.algorithms import CplexOptimizer from qiskit_optimization.problems import QuadraticProgram @@ -24,20 +27,20 @@ class TestCplexOptimizer(QiskitOptimizationTestCase): """Cplex Optimizer Tests.""" @data( - ('op_ip1.lp', [0, 2], 6), - ('op_mip1.lp', [1, 1, 0], 6), - ('op_lp1.lp', [0.25, 1.75], 5.8750) + ("op_ip1.lp", [0, 2], 6), + ("op_mip1.lp", [1, 1, 0], 6), + ("op_lp1.lp", [0.25, 1.75], 5.8750), ) @requires_extra_library def test_cplex_optimizer(self, config): - """ Cplex Optimizer Test """ + """Cplex Optimizer Test""" cplex_optimizer = CplexOptimizer(disp=False) # unpack configuration filename, x, fval = config # load optimization problem problem = QuadraticProgram() - lp_file = self.get_resource_path(filename, 'algorithms/resources') + lp_file = self.get_resource_path(filename, "algorithms/resources") problem.read_from_lp_file(lp_file) # solve problem with cplex @@ -49,5 +52,5 @@ def test_cplex_optimizer(self, config): self.assertAlmostEqual(result.x[i], x[i]) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/algorithms/test_goemans_williamson_optimizer.py b/test/algorithms/test_goemans_williamson_optimizer.py index 4cc4831cf..91ea08e97 100644 --- a/test/algorithms/test_goemans_williamson_optimizer.py +++ b/test/algorithms/test_goemans_williamson_optimizer.py @@ -15,8 +15,10 @@ import numpy as np -from qiskit_optimization.algorithms.goemans_williamson_optimizer \ - import (GoemansWilliamsonOptimizer, GoemansWilliamsonOptimizationResult) +from qiskit_optimization.algorithms.goemans_williamson_optimizer import ( + GoemansWilliamsonOptimizer, + GoemansWilliamsonOptimizationResult, +) from qiskit_optimization.applications.max_cut import Maxcut @@ -26,10 +28,14 @@ class TestGoemansWilliamson(QiskitOptimizationTestCase): @requires_extra_library def test_all_cuts(self): """Basic test of the Goemans-Williamson optimizer.""" - graph = np.array([[0., 1., 2., 0.], - [1., 0., 1., 0.], - [2., 1., 0., 1.], - [0., 0., 1., 0.]]) + graph = np.array( + [ + [0.0, 1.0, 2.0, 0.0], + [1.0, 0.0, 1.0, 0.0], + [2.0, 1.0, 0.0, 1.0], + [0.0, 0.0, 1.0, 0.0], + ] + ) optimizer = GoemansWilliamsonOptimizer(num_cuts=10, seed=0) diff --git a/test/algorithms/test_grover_optimizer.py b/test/algorithms/test_grover_optimizer.py index 0047a9628..c43d58723 100644 --- a/test/algorithms/test_grover_optimizer.py +++ b/test/algorithms/test_grover_optimizer.py @@ -21,13 +21,17 @@ from qiskit import Aer from qiskit.utils import QuantumInstance, algorithm_globals from qiskit.algorithms import NumPyMinimumEigensolver -from qiskit_optimization.algorithms import (GroverOptimizer, - MinimumEigenOptimizer, - OptimizationResultStatus) -from qiskit_optimization.converters import (InequalityToEquality, - IntegerToBinary, - LinearEqualityToPenalty, - QuadraticProgramToQubo) +from qiskit_optimization.algorithms import ( + GroverOptimizer, + MinimumEigenOptimizer, + OptimizationResultStatus, +) +from qiskit_optimization.converters import ( + InequalityToEquality, + IntegerToBinary, + LinearEqualityToPenalty, + QuadraticProgramToQubo, +) from qiskit_optimization.problems import QuadraticProgram @@ -38,10 +42,14 @@ class TestGroverOptimizer(QiskitOptimizationTestCase): def setUp(self): super().setUp() algorithm_globals.random_seed = 1 - self.sv_simulator = QuantumInstance(Aer.get_backend('statevector_simulator'), - seed_simulator=921, seed_transpiler=200) - self.qasm_simulator = QuantumInstance(Aer.get_backend('qasm_simulator'), - seed_simulator=123, seed_transpiler=123) + self.sv_simulator = QuantumInstance( + Aer.get_backend("statevector_simulator"), + seed_simulator=921, + seed_transpiler=200, + ) + self.qasm_simulator = QuantumInstance( + Aer.get_backend("qasm_simulator"), seed_simulator=123, seed_transpiler=123 + ) def validate_results(self, problem, results): """Validate the results object returned by GroverOptimizer.""" @@ -59,9 +67,9 @@ def test_qubo_gas_int_zero(self): # Input. model = Model() - x_0 = model.binary_var(name='x0') - x_1 = model.binary_var(name='x1') - model.minimize(0*x_0+0*x_1) + x_0 = model.binary_var(name="x0") + x_1 = model.binary_var(name="x1") + model.minimize(0 * x_0 + 0 * x_1) op = QuadraticProgram() op.from_docplex(model) @@ -77,15 +85,17 @@ def test_qubo_gas_int_simple(self): # Input. model = Model() - x_0 = model.binary_var(name='x0') - x_1 = model.binary_var(name='x1') - model.minimize(-x_0+2*x_1) + x_0 = model.binary_var(name="x0") + x_1 = model.binary_var(name="x1") + model.minimize(-x_0 + 2 * x_1) op = QuadraticProgram() op.from_docplex(model) # Get the optimum key and value. n_iter = 8 - gmf = GroverOptimizer(4, num_iterations=n_iter, quantum_instance=self.sv_simulator) + gmf = GroverOptimizer( + 4, num_iterations=n_iter, quantum_instance=self.sv_simulator + ) results = gmf.solve(op) self.validate_results(op, results) @@ -98,19 +108,21 @@ def test_qubo_gas_int_simple_maximize(self): # Input. model = Model() - x_0 = model.binary_var(name='x0') - x_1 = model.binary_var(name='x1') - model.maximize(-x_0+2*x_1) + x_0 = model.binary_var(name="x0") + x_1 = model.binary_var(name="x1") + model.maximize(-x_0 + 2 * x_1) op = QuadraticProgram() op.from_docplex(model) # Get the optimum key and value. n_iter = 8 - gmf = GroverOptimizer(4, num_iterations=n_iter, quantum_instance=self.sv_simulator) + gmf = GroverOptimizer( + 4, num_iterations=n_iter, quantum_instance=self.sv_simulator + ) results = gmf.solve(op) self.validate_results(op, results) - @data('sv', 'qasm') + @data("sv", "qasm") def test_qubo_gas_int_paper_example(self, simulator): """ Test the example from https://arxiv.org/abs/1912.04088 using the state vector simulator @@ -119,17 +131,17 @@ def test_qubo_gas_int_paper_example(self, simulator): # Input. model = Model() - x_0 = model.binary_var(name='x0') - x_1 = model.binary_var(name='x1') - x_2 = model.binary_var(name='x2') - model.minimize(-x_0+2*x_1-3*x_2-2*x_0*x_2-1*x_1*x_2) + x_0 = model.binary_var(name="x0") + x_1 = model.binary_var(name="x1") + x_2 = model.binary_var(name="x2") + model.minimize(-x_0 + 2 * x_1 - 3 * x_2 - 2 * x_0 * x_2 - 1 * x_1 * x_2) op = QuadraticProgram() op.from_docplex(model) # Get the optimum key and value. n_iter = 10 - q_instance = self.sv_simulator if simulator == 'sv' else self.qasm_simulator + q_instance = self.sv_simulator if simulator == "sv" else self.qasm_simulator gmf = GroverOptimizer(6, num_iterations=n_iter, quantum_instance=q_instance) results = gmf.solve(op) self.validate_results(op, results) @@ -138,9 +150,9 @@ def test_converter_list(self): """Test converters list""" # Input. model = Model() - x_0 = model.binary_var(name='x0') - x_1 = model.binary_var(name='x1') - model.maximize(-x_0+2*x_1) + x_0 = model.binary_var(name="x0") + x_1 = model.binary_var(name="x1") + model.maximize(-x_0 + 2 * x_1) op = QuadraticProgram() op.from_docplex(model) @@ -148,8 +160,12 @@ def test_converter_list(self): n_iter = 8 # a single converter. qp2qubo = QuadraticProgramToQubo() - gmf = GroverOptimizer(4, num_iterations=n_iter, quantum_instance=self.sv_simulator, - converters=qp2qubo) + gmf = GroverOptimizer( + 4, + num_iterations=n_iter, + quantum_instance=self.sv_simulator, + converters=qp2qubo, + ) results = gmf.solve(op) self.validate_results(op, results) # a list of converters @@ -157,36 +173,46 @@ def test_converter_list(self): int2bin = IntegerToBinary() penalize = LinearEqualityToPenalty() converters = [ineq2eq, int2bin, penalize] - gmf = GroverOptimizer(4, num_iterations=n_iter, quantum_instance=self.sv_simulator, - converters=converters) + gmf = GroverOptimizer( + 4, + num_iterations=n_iter, + quantum_instance=self.sv_simulator, + converters=converters, + ) results = gmf.solve(op) self.validate_results(op, results) # invalid converters with self.assertRaises(TypeError): invalid = [qp2qubo, "invalid converter"] - GroverOptimizer(4, num_iterations=n_iter, - quantum_instance=self.sv_simulator, - converters=invalid) + GroverOptimizer( + 4, + num_iterations=n_iter, + quantum_instance=self.sv_simulator, + converters=invalid, + ) def test_samples_and_raw_samples(self): """Test samples and raw_samples""" op = QuadraticProgram() - op.integer_var(0, 3, 'x') - op.binary_var('y') - op.minimize(linear={'x': 1, 'y': 2}) - op.linear_constraint(linear={'x': 1, 'y': 1}, sense='>=', rhs=1, name='xy') + op.integer_var(0, 3, "x") + op.binary_var("y") + op.minimize(linear={"x": 1, "y": 2}) + op.linear_constraint(linear={"x": 1, "y": 1}, sense=">=", rhs=1, name="xy") opt_sol = 1 success = OptimizationResultStatus.SUCCESS algorithm_globals.random_seed = 1 grover_optimizer = GroverOptimizer( - 8, num_iterations=5, quantum_instance=self.qasm_simulator) + 8, num_iterations=5, quantum_instance=self.qasm_simulator + ) result = grover_optimizer.solve(op) self.assertEqual(len(result.samples), 8) self.assertEqual(len(result.raw_samples), 32) self.assertAlmostEqual(sum(s.probability for s in result.samples), 1) self.assertAlmostEqual(sum(s.probability for s in result.raw_samples), 1) self.assertAlmostEqual(min(s.fval for s in result.samples), 0) - self.assertAlmostEqual(min(s.fval for s in result.samples if s.status == success), opt_sol) + self.assertAlmostEqual( + min(s.fval for s in result.samples if s.status == success), opt_sol + ) self.assertAlmostEqual(min(s.fval for s in result.raw_samples), opt_sol) for sample in result.raw_samples: self.assertEqual(sample.status, success) @@ -195,5 +221,5 @@ def test_samples_and_raw_samples(self): self.assertEqual(result.status, result.raw_samples[0].status) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/algorithms/test_min_eigen_optimizer.py b/test/algorithms/test_min_eigen_optimizer.py index 2a65cfaa9..f223a03e5 100644 --- a/test/algorithms/test_min_eigen_optimizer.py +++ b/test/algorithms/test_min_eigen_optimizer.py @@ -13,7 +13,10 @@ """ Test Min Eigen Optimizer """ import unittest -from test.optimization_test_case import QiskitOptimizationTestCase, requires_extra_library +from test.optimization_test_case import ( + QiskitOptimizationTestCase, + requires_extra_library, +) import numpy as np from ddt import data, ddt @@ -22,10 +25,16 @@ from qiskit.utils import QuantumInstance, algorithm_globals from qiskit.algorithms import QAOA, NumPyMinimumEigensolver from qiskit.algorithms.optimizers import COBYLA -from qiskit_optimization.algorithms import (CplexOptimizer, MinimumEigenOptimizer) -from qiskit_optimization.algorithms.optimization_algorithm import OptimizationResultStatus -from qiskit_optimization.converters import (InequalityToEquality, IntegerToBinary, - LinearEqualityToPenalty, QuadraticProgramToQubo) +from qiskit_optimization.algorithms import CplexOptimizer, MinimumEigenOptimizer +from qiskit_optimization.algorithms.optimization_algorithm import ( + OptimizationResultStatus, +) +from qiskit_optimization.converters import ( + InequalityToEquality, + IntegerToBinary, + LinearEqualityToPenalty, + QuadraticProgramToQubo, +) from qiskit_optimization.problems import QuadraticProgram @@ -40,20 +49,20 @@ def setUp(self): self.min_eigen_solvers = {} # exact eigen solver - self.min_eigen_solvers['exact'] = NumPyMinimumEigensolver() + self.min_eigen_solvers["exact"] = NumPyMinimumEigensolver() # QAOA optimizer = COBYLA() - self.min_eigen_solvers['qaoa'] = QAOA(optimizer=optimizer) + self.min_eigen_solvers["qaoa"] = QAOA(optimizer=optimizer) @data( - ('exact', None, 'op_ip1.lp'), - ('qaoa', 'statevector_simulator', 'op_ip1.lp'), - ('qaoa', 'qasm_simulator', 'op_ip1.lp') + ("exact", None, "op_ip1.lp"), + ("qaoa", "statevector_simulator", "op_ip1.lp"), + ("qaoa", "qasm_simulator", "op_ip1.lp"), ) @requires_extra_library def test_min_eigen_optimizer(self, config): - """ Min Eigen Optimizer Test """ + """Min Eigen Optimizer Test""" try: # unpack configuration min_eigen_solver_name, backend, filename = config @@ -68,7 +77,7 @@ def test_min_eigen_optimizer(self, config): # load optimization problem problem = QuadraticProgram() - lp_file = self.get_resource_path(filename, 'algorithms/resources') + lp_file = self.get_resource_path(filename, "algorithms/resources") problem.read_from_lp_file(lp_file) # solve problem with cplex @@ -88,12 +97,12 @@ def test_min_eigen_optimizer(self, config): self.fail(str(ex)) @data( - ('op_ip1.lp', -470, 12, OptimizationResultStatus.SUCCESS), - ('op_ip1.lp', np.inf, None, OptimizationResultStatus.FAILURE), + ("op_ip1.lp", -470, 12, OptimizationResultStatus.SUCCESS), + ("op_ip1.lp", np.inf, None, OptimizationResultStatus.FAILURE), ) @requires_extra_library def test_min_eigen_optimizer_with_filter(self, config): - """ Min Eigen Optimizer Test """ + """Min Eigen Optimizer Test""" try: # unpack configuration filename, lowerbound, fval, status = config @@ -113,7 +122,7 @@ def filter_criterion(x, v, aux): # load optimization problem problem = QuadraticProgram() - lp_file = self.get_resource_path(filename, 'algorithms/resources') + lp_file = self.get_resource_path(filename, "algorithms/resources") problem.read_from_lp_file(lp_file) # solve problem @@ -133,14 +142,16 @@ def test_converter_list(self): """Test converter list""" op = QuadraticProgram() op.integer_var(0, 3, "x") - op.binary_var('y') + op.binary_var("y") - op.maximize(linear={'x': 1, 'y': 2}) - op.linear_constraint(linear={'x': 1, 'y': 1}, sense='LE', rhs=3, name='xy_leq') + op.maximize(linear={"x": 1, "y": 2}) + op.linear_constraint(linear={"x": 1, "y": 1}, sense="LE", rhs=3, name="xy_leq") min_eigen_solver = NumPyMinimumEigensolver() # a single converter qp2qubo = QuadraticProgramToQubo() - min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver, converters=qp2qubo) + min_eigen_optimizer = MinimumEigenOptimizer( + min_eigen_solver, converters=qp2qubo + ) result = min_eigen_optimizer.solve(op) self.assertEqual(result.fval, 4) # a list of converters @@ -148,28 +159,32 @@ def test_converter_list(self): int2bin = IntegerToBinary() penalize = LinearEqualityToPenalty() converters = [ineq2eq, int2bin, penalize] - min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver, converters=converters) + min_eigen_optimizer = MinimumEigenOptimizer( + min_eigen_solver, converters=converters + ) result = min_eigen_optimizer.solve(op) self.assertEqual(result.fval, 4) with self.assertRaises(TypeError): invalid = [qp2qubo, "invalid converter"] - MinimumEigenOptimizer(min_eigen_solver, - converters=invalid) + MinimumEigenOptimizer(min_eigen_solver, converters=invalid) def test_samples(self): """Test samples""" SUCCESS = OptimizationResultStatus.SUCCESS # pylint: disable=invalid-name algorithm_globals.random_seed = 123 - quantum_instance = QuantumInstance(backend=BasicAer.get_backend('qasm_simulator'), - seed_simulator=123, seed_transpiler=123, - shots=1000) + quantum_instance = QuantumInstance( + backend=BasicAer.get_backend("qasm_simulator"), + seed_simulator=123, + seed_transpiler=123, + shots=1000, + ) # test minimize op = QuadraticProgram() - op.integer_var(0, 3, 'x') - op.binary_var('y') - op.minimize(linear={'x': 1, 'y': 2}) - op.linear_constraint(linear={'x': 1, 'y': 1}, sense='>=', rhs=1, name='xy') + op.integer_var(0, 3, "x") + op.binary_var("y") + op.minimize(linear={"x": 1, "y": 2}) + op.linear_constraint(linear={"x": 1, "y": 1}, sense=">=", rhs=1, name="xy") min_eigen_solver = NumPyMinimumEigensolver() min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) @@ -195,7 +210,9 @@ def test_samples(self): self.assertAlmostEqual(sum(s.probability for s in result.samples), 1) self.assertAlmostEqual(sum(s.probability for s in result.raw_samples), 1) self.assertAlmostEqual(min(s.fval for s in result.samples), 0) - self.assertAlmostEqual(min(s.fval for s in result.samples if s.status == SUCCESS), opt_sol) + self.assertAlmostEqual( + min(s.fval for s in result.samples if s.status == SUCCESS), opt_sol + ) self.assertAlmostEqual(min(s.fval for s in result.raw_samples), opt_sol) for sample in result.raw_samples: self.assertEqual(sample.status, SUCCESS) @@ -205,10 +222,10 @@ def test_samples(self): # test maximize op = QuadraticProgram() - op.integer_var(0, 3, 'x') - op.binary_var('y') - op.maximize(linear={'x': 1, 'y': 2}) - op.linear_constraint(linear={'x': 1, 'y': 1}, sense='<=', rhs=1, name='xy') + op.integer_var(0, 3, "x") + op.binary_var("y") + op.maximize(linear={"x": 1, "y": 2}) + op.linear_constraint(linear={"x": 1, "y": 1}, sense="<=", rhs=1, name="xy") min_eigen_solver = NumPyMinimumEigensolver() min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) @@ -234,7 +251,9 @@ def test_samples(self): self.assertAlmostEqual(sum(s.probability for s in result.samples), 1) self.assertAlmostEqual(sum(s.probability for s in result.raw_samples), 1) self.assertAlmostEqual(max(s.fval for s in result.samples), 5) - self.assertAlmostEqual(max(s.fval for s in result.samples if s.status == SUCCESS), opt_sol) + self.assertAlmostEqual( + max(s.fval for s in result.samples if s.status == SUCCESS), opt_sol + ) self.assertAlmostEqual(max(s.fval for s in result.raw_samples), opt_sol) for sample in result.raw_samples: self.assertEqual(sample.status, SUCCESS) @@ -243,5 +262,5 @@ def test_samples(self): self.assertEqual(result.status, result.samples[0].status) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/algorithms/test_recursive_optimization.py b/test/algorithms/test_recursive_optimization.py index 2fa7ae348..bd1eeb859 100755 --- a/test/algorithms/test_recursive_optimization.py +++ b/test/algorithms/test_recursive_optimization.py @@ -22,13 +22,23 @@ from qiskit.algorithms import NumPyMinimumEigensolver, QAOA -from qiskit_optimization.algorithms import (MinimumEigenOptimizer, CplexOptimizer, - RecursiveMinimumEigenOptimizer, WarmStartQAOAOptimizer, - SlsqpOptimizer) -from qiskit_optimization.algorithms.recursive_minimum_eigen_optimizer import IntermediateResult +from qiskit_optimization.algorithms import ( + MinimumEigenOptimizer, + CplexOptimizer, + RecursiveMinimumEigenOptimizer, + WarmStartQAOAOptimizer, + SlsqpOptimizer, +) +from qiskit_optimization.algorithms.recursive_minimum_eigen_optimizer import ( + IntermediateResult, +) from qiskit_optimization.problems import QuadraticProgram -from qiskit_optimization.converters import (IntegerToBinary, InequalityToEquality, - LinearEqualityToPenalty, QuadraticProgramToQubo) +from qiskit_optimization.converters import ( + IntegerToBinary, + InequalityToEquality, + LinearEqualityToPenalty, + QuadraticProgramToQubo, +) class TestRecursiveMinEigenOptimizer(QiskitOptimizationTestCase): @@ -37,18 +47,19 @@ class TestRecursiveMinEigenOptimizer(QiskitOptimizationTestCase): @requires_extra_library def test_recursive_min_eigen_optimizer(self): """Test the recursive minimum eigen optimizer.""" - filename = 'op_ip1.lp' + filename = "op_ip1.lp" # get minimum eigen solver min_eigen_solver = NumPyMinimumEigensolver() # construct minimum eigen optimizer min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) - recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer(min_eigen_optimizer, - min_num_vars=4) + recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer( + min_eigen_optimizer, min_num_vars=4 + ) # load optimization problem problem = QuadraticProgram() - lp_file = self.get_resource_path(filename, 'algorithms/resources') + lp_file = self.get_resource_path(filename, "algorithms/resources") problem.read_from_lp_file(lp_file) # solve problem with cplex @@ -65,10 +76,10 @@ def test_recursive_min_eigen_optimizer(self): @requires_extra_library def test_recursive_history(self): """Tests different options for history.""" - filename = 'op_ip1.lp' + filename = "op_ip1.lp" # load optimization problem problem = QuadraticProgram() - lp_file = self.get_resource_path(filename, 'algorithms/resources') + lp_file = self.get_resource_path(filename, "algorithms/resources") problem.read_from_lp_file(lp_file) # get minimum eigen solver @@ -78,10 +89,11 @@ def test_recursive_history(self): min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) # no history - recursive_min_eigen_optimizer = \ - RecursiveMinimumEigenOptimizer(min_eigen_optimizer, - min_num_vars=4, - history=IntermediateResult.NO_ITERATIONS) + recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer( + min_eigen_optimizer, + min_num_vars=4, + history=IntermediateResult.NO_ITERATIONS, + ) result = recursive_min_eigen_optimizer.solve(problem) self.assertIsNotNone(result.replacements) self.assertIsNotNone(result.history) @@ -90,10 +102,11 @@ def test_recursive_history(self): self.assertIsNone(result.history[1]) # only last iteration in the history - recursive_min_eigen_optimizer = \ - RecursiveMinimumEigenOptimizer(min_eigen_optimizer, - min_num_vars=4, - history=IntermediateResult.LAST_ITERATION) + recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer( + min_eigen_optimizer, + min_num_vars=4, + history=IntermediateResult.LAST_ITERATION, + ) result = recursive_min_eigen_optimizer.solve(problem) self.assertIsNotNone(result.replacements) self.assertIsNotNone(result.history) @@ -102,10 +115,11 @@ def test_recursive_history(self): self.assertIsNotNone(result.history[1]) # full history - recursive_min_eigen_optimizer = \ - RecursiveMinimumEigenOptimizer(min_eigen_optimizer, - min_num_vars=4, - history=IntermediateResult.ALL_ITERATIONS) + recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer( + min_eigen_optimizer, + min_num_vars=4, + history=IntermediateResult.ALL_ITERATIONS, + ) result = recursive_min_eigen_optimizer.solve(problem) self.assertIsNotNone(result.replacements) self.assertIsNotNone(result.history) @@ -119,15 +133,17 @@ def test_recursive_warm_qaoa(self): algorithm_globals.random_seed = 12345 backend = BasicAer.get_backend("statevector_simulator") qaoa = QAOA(quantum_instance=backend, reps=1) - warm_qaoa = WarmStartQAOAOptimizer(pre_solver=SlsqpOptimizer(), - relax_for_pre_solver=True, qaoa=qaoa) + warm_qaoa = WarmStartQAOAOptimizer( + pre_solver=SlsqpOptimizer(), relax_for_pre_solver=True, qaoa=qaoa + ) recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer( - warm_qaoa, min_num_vars=4) + warm_qaoa, min_num_vars=4 + ) # load optimization problem problem = QuadraticProgram() - lp_file = self.get_resource_path('op_ip1.lp', 'algorithms/resources') + lp_file = self.get_resource_path("op_ip1.lp", "algorithms/resources") problem.read_from_lp_file(lp_file) # solve problem with cplex @@ -145,19 +161,19 @@ def test_converter_list(self): """Test converter list""" op = QuadraticProgram() op.integer_var(0, 3, "x") - op.binary_var('y') + op.binary_var("y") - op.maximize(linear={'x': 1, 'y': 2}) - op.linear_constraint(linear={'y': 1, 'x': 1}, sense='LE', rhs=3, name='xy_leq') + op.maximize(linear={"x": 1, "y": 2}) + op.linear_constraint(linear={"y": 1, "x": 1}, sense="LE", rhs=3, name="xy_leq") # construct minimum eigen optimizer min_eigen_solver = NumPyMinimumEigensolver() min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) # a single converter qp2qubo = QuadraticProgramToQubo() - recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer(min_eigen_optimizer, - min_num_vars=2, - converters=qp2qubo) + recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer( + min_eigen_optimizer, min_num_vars=2, converters=qp2qubo + ) result = recursive_min_eigen_optimizer.solve(op) self.assertEqual(result.fval, 4) # a list of converters @@ -165,18 +181,18 @@ def test_converter_list(self): int2bin = IntegerToBinary() penalize = LinearEqualityToPenalty() converters = [ineq2eq, int2bin, penalize] - recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer(min_eigen_optimizer, - min_num_vars=2, - converters=converters) + recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer( + min_eigen_optimizer, min_num_vars=2, converters=converters + ) result = recursive_min_eigen_optimizer.solve(op) self.assertEqual(result.fval, 4) # invalid converters with self.assertRaises(TypeError): invalid = [qp2qubo, "invalid converter"] - RecursiveMinimumEigenOptimizer(min_eigen_optimizer, - min_num_vars=2, - converters=invalid) + RecursiveMinimumEigenOptimizer( + min_eigen_optimizer, min_num_vars=2, converters=invalid + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/algorithms/test_slsqp.py b/test/algorithms/test_slsqp.py index 3b51180e2..cb8522dfb 100644 --- a/test/algorithms/test_slsqp.py +++ b/test/algorithms/test_slsqp.py @@ -23,15 +23,15 @@ class TestSlsqpOptimizer(QiskitOptimizationTestCase): - """SLSQP Optimizer Tests. """ + """SLSQP Optimizer Tests.""" def test_slsqp_optimizer(self): - """ Generic SLSQP Optimizer Test. """ + """Generic SLSQP Optimizer Test.""" problem = QuadraticProgram() problem.continuous_var(upperbound=4) problem.continuous_var(upperbound=4) - problem.linear_constraint(linear=[1, 1], sense='=', rhs=2) + problem.linear_constraint(linear=[1, 1], sense="=", rhs=2) problem.minimize(linear=[2, 2], quadratic=[[2, 0.25], [0.25, 0.5]]) # solve problem with SLSQP @@ -41,12 +41,12 @@ def test_slsqp_optimizer(self): self.assertAlmostEqual(result.fval, 5.8750) def test_slsqp_optimizer_full_output(self): - """ Generic SLSQP Optimizer Test. """ + """Generic SLSQP Optimizer Test.""" problem = QuadraticProgram() problem.continuous_var(upperbound=4) problem.continuous_var(upperbound=4) - problem.linear_constraint(linear=[1, 1], sense='=', rhs=2) + problem.linear_constraint(linear=[1, 1], sense="=", rhs=2) problem.minimize(linear=[2, 2], quadratic=[[2, 0.25], [0.25, 0.5]]) # solve problem with SLSQP @@ -77,9 +77,9 @@ def test_slsqp_unbounded(self): self.assertIsNotNone(solution) self.assertIsNotNone(solution.x) - np.testing.assert_almost_equal([2., 1.], solution.x, 3) + np.testing.assert_almost_equal([2.0, 1.0], solution.x, 3) self.assertIsNotNone(solution.fval) - np.testing.assert_almost_equal(2., solution.fval, 3) + np.testing.assert_almost_equal(2.0, solution.fval, 3) def test_slsqp_unbounded_with_trials(self): """Unbounded test for optimization""" @@ -93,9 +93,9 @@ def test_slsqp_unbounded_with_trials(self): self.assertIsNotNone(solution) self.assertIsNotNone(solution.x) - np.testing.assert_almost_equal([2., 1.], solution.x, 3) + np.testing.assert_almost_equal([2.0, 1.0], solution.x, 3) self.assertIsNotNone(solution.fval) - np.testing.assert_almost_equal(2., solution.fval, 3) + np.testing.assert_almost_equal(2.0, solution.fval, 3) def test_slsqp_bounded(self): """Same as above, but a bounded test""" @@ -118,7 +118,7 @@ def test_slsqp_equality(self): problem = QuadraticProgram() problem.continuous_var(name="x") problem.continuous_var(name="y") - problem.linear_constraint(linear=[1, -1], sense='=', rhs=0) + problem.linear_constraint(linear=[1, -1], sense="=", rhs=0) problem.maximize(linear=[2, 0], quadratic=[[-1, 2], [0, -2]]) slsqp = SlsqpOptimizer() @@ -126,16 +126,16 @@ def test_slsqp_equality(self): self.assertIsNotNone(solution) self.assertIsNotNone(solution.x) - np.testing.assert_almost_equal([1., 1.], solution.x, 3) + np.testing.assert_almost_equal([1.0, 1.0], solution.x, 3) self.assertIsNotNone(solution.fval) - np.testing.assert_almost_equal(1., solution.fval, 3) + np.testing.assert_almost_equal(1.0, solution.fval, 3) def test_slsqp_inequality(self): """A test with inequality constraint""" problem = QuadraticProgram() problem.continuous_var(name="x") problem.continuous_var(name="y") - problem.linear_constraint(linear=[1, -1], sense='>=', rhs=1) + problem.linear_constraint(linear=[1, -1], sense=">=", rhs=1) problem.maximize(linear=[2, 0], quadratic=[[-1, 2], [0, -2]]) slsqp = SlsqpOptimizer() @@ -143,9 +143,9 @@ def test_slsqp_inequality(self): self.assertIsNotNone(solution) self.assertIsNotNone(solution.x) - np.testing.assert_almost_equal([2., 1.], solution.x, 3) + np.testing.assert_almost_equal([2.0, 1.0], solution.x, 3) self.assertIsNotNone(solution.fval) - np.testing.assert_almost_equal(2., solution.fval, 3) + np.testing.assert_almost_equal(2.0, solution.fval, 3) def test_slsqp_optimizer_with_quadratic_constraint(self): """A test with equality constraint""" @@ -166,7 +166,7 @@ def test_slsqp_optimizer_with_quadratic_constraint(self): self.assertIsNotNone(solution.x) np.testing.assert_almost_equal([0.5, 0.5], solution.x, 3) self.assertIsNotNone(solution.fval) - np.testing.assert_almost_equal(1., solution.fval, 3) + np.testing.assert_almost_equal(1.0, solution.fval, 3) def test_multistart_properties(self): """ @@ -174,19 +174,19 @@ def test_multistart_properties(self): Since it is an abstract class, the test is here. """ trials = 5 - clip = 200. + clip = 200.0 slsqp = SlsqpOptimizer(trials=trials, clip=clip) self.assertEqual(trials, slsqp.trials) self.assertAlmostEqual(clip, slsqp.clip) trials = 6 - clip = 300. + clip = 300.0 slsqp.trials = trials slsqp.clip = clip self.assertEqual(trials, slsqp.trials) self.assertAlmostEqual(clip, slsqp.clip) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/algorithms/test_warm_start_qaoa.py b/test/algorithms/test_warm_start_qaoa.py index eeb13feee..78b336117 100644 --- a/test/algorithms/test_warm_start_qaoa.py +++ b/test/algorithms/test_warm_start_qaoa.py @@ -21,9 +21,13 @@ from qiskit_optimization import QuadraticProgram from qiskit_optimization.algorithms import SlsqpOptimizer -from qiskit_optimization.algorithms.goemans_williamson_optimizer import GoemansWilliamsonOptimizer -from qiskit_optimization.algorithms.warm_start_qaoa_optimizer import MeanAggregator, \ - WarmStartQAOAOptimizer +from qiskit_optimization.algorithms.goemans_williamson_optimizer import ( + GoemansWilliamsonOptimizer, +) +from qiskit_optimization.algorithms.warm_start_qaoa_optimizer import ( + MeanAggregator, + WarmStartQAOAOptimizer, +) from qiskit_optimization.applications.max_cut import Maxcut @@ -33,10 +37,14 @@ class TestWarmStartQAOAOptimizer(QiskitOptimizationTestCase): @requires_extra_library def test_max_cut(self): """Basic test on the max cut problem.""" - graph = np.array([[0., 1., 2., 0.], - [1., 0., 1., 0.], - [2., 1., 0., 1.], - [0., 0., 1., 0.]]) + graph = np.array( + [ + [0.0, 1.0, 2.0, 0.0], + [1.0, 0.0, 1.0, 0.0], + [2.0, 1.0, 0.0, 1.0], + [0.0, 0.0, 1.0, 0.0], + ] + ) presolver = GoemansWilliamsonOptimizer(num_cuts=10) problem = Maxcut(graph).to_quadratic_program() @@ -44,9 +52,14 @@ def test_max_cut(self): backend = BasicAer.get_backend("statevector_simulator") qaoa = QAOA(quantum_instance=backend, reps=1) aggregator = MeanAggregator() - optimizer = WarmStartQAOAOptimizer(pre_solver=presolver, relax_for_pre_solver=False, - qaoa=qaoa, epsilon=0.25, num_initial_solutions=10, - aggregator=aggregator) + optimizer = WarmStartQAOAOptimizer( + pre_solver=presolver, + relax_for_pre_solver=False, + qaoa=qaoa, + epsilon=0.25, + num_initial_solutions=10, + aggregator=aggregator, + ) result_warm = optimizer.solve(problem) self.assertIsNotNone(result_warm) @@ -73,8 +86,13 @@ def test_constrained_binary(self): backend = BasicAer.get_backend("statevector_simulator") qaoa = QAOA(quantum_instance=backend, reps=1) aggregator = MeanAggregator() - optimizer = WarmStartQAOAOptimizer(pre_solver=SlsqpOptimizer(), relax_for_pre_solver=True, - qaoa=qaoa, epsilon=0.25, aggregator=aggregator) + optimizer = WarmStartQAOAOptimizer( + pre_solver=SlsqpOptimizer(), + relax_for_pre_solver=True, + qaoa=qaoa, + epsilon=0.25, + aggregator=aggregator, + ) result_warm = optimizer.solve(problem) self.assertIsNotNone(result_warm) @@ -96,8 +114,12 @@ def test_simple_qubo(self): backend = BasicAer.get_backend("statevector_simulator") qaoa = QAOA(quantum_instance=backend, reps=1) - optimizer = WarmStartQAOAOptimizer(pre_solver=SlsqpOptimizer(), relax_for_pre_solver=True, - qaoa=qaoa, epsilon=0.25) + optimizer = WarmStartQAOAOptimizer( + pre_solver=SlsqpOptimizer(), + relax_for_pre_solver=True, + qaoa=qaoa, + epsilon=0.25, + ) result_warm = optimizer.solve(problem) self.assertIsNotNone(result_warm) diff --git a/test/applications/test_clique.py b/test/applications/test_clique.py index 55004c2bc..30eb585f7 100644 --- a/test/applications/test_clique.py +++ b/test/applications/test_clique.py @@ -16,14 +16,13 @@ import networkx as nx from qiskit_optimization import QuadraticProgram -from qiskit_optimization.algorithms import (OptimizationResult, - OptimizationResultStatus) +from qiskit_optimization.algorithms import OptimizationResult, OptimizationResultStatus from qiskit_optimization.applications.clique import Clique -from qiskit_optimization.problems import (Constraint, QuadraticObjective, VarType) +from qiskit_optimization.problems import Constraint, QuadraticObjective, VarType class TestClique(QiskitOptimizationTestCase): - """ Test Clique class""" + """Test Clique class""" def setUp(self): """Set up for the tests""" @@ -33,11 +32,17 @@ def setUp(self): for _ in range(5): op.binary_var() self.result = OptimizationResult( - x=[1, 0, 1, 1, 1], fval=4, variables=op.variables, - status=OptimizationResultStatus.SUCCESS) + x=[1, 0, 1, 1, 1], + fval=4, + variables=op.variables, + status=OptimizationResultStatus.SUCCESS, + ) self.result_c3 = OptimizationResult( - x=[1, 0, 1, 1, 0], fval=0, variables=op.variables, - status=OptimizationResultStatus.SUCCESS) + x=[1, 0, 1, 1, 0], + fval=0, + variables=op.variables, + status=OptimizationResultStatus.SUCCESS, + ) def test_to_quadratic_program(self): """Test to_quadratic_program""" @@ -53,7 +58,9 @@ def test_to_quadratic_program(self): obj = op.objective self.assertEqual(obj.sense, QuadraticObjective.Sense.MAXIMIZE) self.assertEqual(obj.constant, 0) - self.assertDictEqual(obj.linear.to_dict(), {0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0}) + self.assertDictEqual( + obj.linear.to_dict(), {0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0} + ) self.assertDictEqual(obj.quadratic.to_dict(), {}) # Test constraint lin = op.linear_constraints @@ -73,7 +80,9 @@ def test_interpret(self): def test_node_colors(self): """Test _node_colors""" clique = Clique(self.graph) - self.assertEqual(clique._node_colors(self.result), ['r', 'darkgrey', 'r', 'r', 'r']) + self.assertEqual( + clique._node_colors(self.result), ["r", "darkgrey", "r", "r", "r"] + ) def test_size(self): """Test size""" @@ -108,7 +117,9 @@ def test_to_quadratic_program_c3(self): self.assertEqual(lin[1].linear.to_dict(), {1: 1.0, 4: 1.0}) self.assertEqual(lin[2].sense, Constraint.Sense.EQ) self.assertEqual(lin[2].rhs, 3) - self.assertEqual(lin[2].linear.to_dict(), {0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0}) + self.assertEqual( + lin[2].linear.to_dict(), {0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0} + ) def test_interpret_c3(self): """Test interpret for the clique size 3""" @@ -119,5 +130,5 @@ def test_node_colors_c3(self): """Test _node_colors for the clique size 3""" clique = Clique(self.graph, 3) self.assertEqual( - clique._node_colors(self.result_c3), - ['r', 'darkgrey', 'r', 'r', 'darkgrey']) + clique._node_colors(self.result_c3), ["r", "darkgrey", "r", "r", "darkgrey"] + ) diff --git a/test/applications/test_exact_cover.py b/test/applications/test_exact_cover.py index acd69497d..e3bcba1ba 100644 --- a/test/applications/test_exact_cover.py +++ b/test/applications/test_exact_cover.py @@ -14,14 +14,13 @@ from test.optimization_test_case import QiskitOptimizationTestCase from qiskit_optimization import QuadraticProgram -from qiskit_optimization.algorithms import (OptimizationResult, - OptimizationResultStatus) +from qiskit_optimization.algorithms import OptimizationResult, OptimizationResultStatus from qiskit_optimization.applications.exact_cover import ExactCover -from qiskit_optimization.problems import (Constraint, QuadraticObjective, VarType) +from qiskit_optimization.problems import Constraint, QuadraticObjective, VarType class TestExactCover(QiskitOptimizationTestCase): - """ Test ExactCover class""" + """Test ExactCover class""" def setUp(self): """Set up for the tests""" @@ -32,8 +31,11 @@ def setUp(self): for _ in range(4): op.binary_var() self.result = OptimizationResult( - x=[0, 1, 1, 0], fval=2, variables=op.variables, - status=OptimizationResultStatus.SUCCESS) + x=[0, 1, 1, 0], + fval=2, + variables=op.variables, + status=OptimizationResultStatus.SUCCESS, + ) def test_to_quadratic_program(self): """Test to_quadratic_program""" @@ -57,9 +59,14 @@ def test_to_quadratic_program(self): for i, lin in enumerate(lin): self.assertEqual(lin.sense, Constraint.Sense.EQ) self.assertEqual(lin.rhs, 1) - self.assertEqual(lin.linear.to_dict(), { - j: 1 for j, subset in enumerate(self.list_of_subsets) - if i+1 in subset}) + self.assertEqual( + lin.linear.to_dict(), + { + j: 1 + for j, subset in enumerate(self.list_of_subsets) + if i + 1 in subset + }, + ) def test_interpret(self): """Test interpret""" diff --git a/test/applications/test_graph_partition.py b/test/applications/test_graph_partition.py index 6c8be2393..785a14bcb 100644 --- a/test/applications/test_graph_partition.py +++ b/test/applications/test_graph_partition.py @@ -15,14 +15,13 @@ import networkx as nx from qiskit_optimization import QuadraticProgram -from qiskit_optimization.algorithms import (OptimizationResult, - OptimizationResultStatus) +from qiskit_optimization.algorithms import OptimizationResult, OptimizationResultStatus from qiskit_optimization.applications.graph_partition import GraphPartition -from qiskit_optimization.problems import (Constraint, QuadraticObjective, VarType) +from qiskit_optimization.problems import Constraint, QuadraticObjective, VarType class TestGraphPartition(QiskitOptimizationTestCase): - """ Test GraphPartitioning class""" + """Test GraphPartitioning class""" def setUp(self): """Set up for the tests""" @@ -32,8 +31,11 @@ def setUp(self): for _ in range(4): op.binary_var() self.result = OptimizationResult( - x=[0, 1, 1, 0], fval=2, variables=op.variables, - status=OptimizationResultStatus.SUCCESS) + x=[0, 1, 1, 0], + fval=2, + variables=op.variables, + status=OptimizationResultStatus.SUCCESS, + ) def test_to_quadratic_program(self): """Test to_quadratic_program""" @@ -50,8 +52,10 @@ def test_to_quadratic_program(self): self.assertEqual(obj.sense, QuadraticObjective.Sense.MINIMIZE) self.assertEqual(obj.constant, 0) self.assertDictEqual(obj.linear.to_dict(), {0: 3.0, 2: 2.0, 3: 1.0, 1: 2.0}) - self.assertDictEqual(obj.quadratic.to_dict(), { - (0, 1): -2.0, (0, 2): -2.0, (1, 2): -2.0, (0, 3): -2.0}) + self.assertDictEqual( + obj.quadratic.to_dict(), + {(0, 1): -2.0, (0, 2): -2.0, (1, 2): -2.0, (0, 3): -2.0}, + ) # Test constraint lin = op.linear_constraints self.assertEqual(len(lin), 1) @@ -67,4 +71,6 @@ def test_interpret(self): def test_node_colors(self): """Test _node_colors""" graph_partitioning = GraphPartition(self.graph) - self.assertEqual(graph_partitioning._node_colors(self.result), ['b', 'r', 'r', 'b']) + self.assertEqual( + graph_partitioning._node_colors(self.result), ["b", "r", "r", "b"] + ) diff --git a/test/applications/test_knapsack.py b/test/applications/test_knapsack.py index 2347ad9c0..5938dc7c3 100644 --- a/test/applications/test_knapsack.py +++ b/test/applications/test_knapsack.py @@ -14,14 +14,13 @@ from test.optimization_test_case import QiskitOptimizationTestCase from qiskit_optimization import QuadraticProgram -from qiskit_optimization.algorithms import (OptimizationResult, - OptimizationResultStatus) +from qiskit_optimization.algorithms import OptimizationResult, OptimizationResultStatus from qiskit_optimization.applications.knapsack import Knapsack -from qiskit_optimization.problems import (Constraint, QuadraticObjective, VarType) +from qiskit_optimization.problems import Constraint, QuadraticObjective, VarType class TestKnapsack(QiskitOptimizationTestCase): - """ Test Knapsack class""" + """Test Knapsack class""" def setUp(self): """Set up for the tests""" @@ -33,12 +32,17 @@ def setUp(self): for _ in range(4): op.binary_var() self.result = OptimizationResult( - x=[0, 1, 0, 1], fval=90, variables=op.variables, - status=OptimizationResultStatus.SUCCESS) + x=[0, 1, 0, 1], + fval=90, + variables=op.variables, + status=OptimizationResultStatus.SUCCESS, + ) def test_to_quadratic_program(self): """Test to_quadratic_program""" - knapsack = Knapsack(values=self.values, weights=self.weights, max_weight=self.max_weight) + knapsack = Knapsack( + values=self.values, weights=self.weights, max_weight=self.max_weight + ) op = knapsack.to_quadratic_program() # Test name self.assertEqual(op.name, "Knapsack") @@ -61,11 +65,15 @@ def test_to_quadratic_program(self): def test_interpret(self): """Test interpret""" - knapsack = Knapsack(values=self.values, weights=self.weights, max_weight=self.max_weight) + knapsack = Knapsack( + values=self.values, weights=self.weights, max_weight=self.max_weight + ) self.assertEqual(knapsack.interpret(self.result), [1, 3]) def test_max_weight(self): """Test max_weight""" - knapsack = Knapsack(values=self.values, weights=self.weights, max_weight=self.max_weight) + knapsack = Knapsack( + values=self.values, weights=self.weights, max_weight=self.max_weight + ) knapsack.max_weight = 5 self.assertEqual(knapsack.max_weight, 5) diff --git a/test/applications/test_max_cut.py b/test/applications/test_max_cut.py index 4884cc18c..39cf0c929 100644 --- a/test/applications/test_max_cut.py +++ b/test/applications/test_max_cut.py @@ -17,14 +17,13 @@ from qiskit.exceptions import MissingOptionalLibraryError from qiskit_optimization import QuadraticProgram -from qiskit_optimization.algorithms import (OptimizationResult, - OptimizationResultStatus) +from qiskit_optimization.algorithms import OptimizationResult, OptimizationResultStatus from qiskit_optimization.applications.max_cut import Maxcut from qiskit_optimization.problems import QuadraticObjective, VarType class TestMaxcut(QiskitOptimizationTestCase): - """ Test Maxcut class""" + """Test Maxcut class""" def setUp(self): super().setUp() @@ -33,8 +32,11 @@ def setUp(self): for _ in range(4): op.binary_var() self.result = OptimizationResult( - x=[1, 1, 0, 0], fval=4, variables=op.variables, - status=OptimizationResultStatus.SUCCESS) + x=[1, 1, 0, 0], + fval=4, + variables=op.variables, + status=OptimizationResultStatus.SUCCESS, + ) def test_to_quadratic_program(self): """Test to_quadratic_program""" @@ -51,8 +53,17 @@ def test_to_quadratic_program(self): self.assertEqual(obj.sense, QuadraticObjective.Sense.MAXIMIZE) self.assertEqual(obj.constant, 0) self.assertDictEqual(obj.linear.to_dict(), {0: 3.0, 1: 3.0, 2: 3.0, 3: 3.0}) - self.assertDictEqual(obj.quadratic.to_dict(), {(0, 1): -2.0, (0, 2): -2.0, (1, 2): -2.0, - (0, 3): -2.0, (1, 3): -2.0, (2, 3): -2.0}) + self.assertDictEqual( + obj.quadratic.to_dict(), + { + (0, 1): -2.0, + (0, 2): -2.0, + (1, 2): -2.0, + (0, 3): -2.0, + (1, 3): -2.0, + (2, 3): -2.0, + }, + ) # Test constraint lin = op.linear_constraints self.assertEqual(len(lin), 0) @@ -65,13 +76,14 @@ def test_interpret(self): def test_node_color(self): """Test _node_color""" maxcut = Maxcut(self.graph) - self.assertEqual(maxcut._node_color(self.result), ['b', 'b', 'r', 'r']) + self.assertEqual(maxcut._node_color(self.result), ["b", "b", "r", "r"]) def test_draw(self): """Test whether draw raises an error if matplotlib is not installed""" maxcut = Maxcut(self.graph) try: import matplotlib as _ + maxcut.draw() except ImportError: diff --git a/test/applications/test_number_partition.py b/test/applications/test_number_partition.py index 1646c3cfd..c9c43ff8a 100644 --- a/test/applications/test_number_partition.py +++ b/test/applications/test_number_partition.py @@ -14,14 +14,13 @@ from test.optimization_test_case import QiskitOptimizationTestCase from qiskit_optimization import QuadraticProgram -from qiskit_optimization.algorithms import (OptimizationResult, - OptimizationResultStatus) +from qiskit_optimization.algorithms import OptimizationResult, OptimizationResultStatus from qiskit_optimization.applications.number_partition import NumberPartition -from qiskit_optimization.problems import (Constraint, QuadraticObjective, VarType) +from qiskit_optimization.problems import Constraint, QuadraticObjective, VarType class TestNumberPartition(QiskitOptimizationTestCase): - """ Test NumberPartition class""" + """Test NumberPartition class""" def setUp(self): """Set up for the test""" @@ -31,8 +30,11 @@ def setUp(self): for _ in range(5): op.binary_var() self.result = OptimizationResult( - x=[1, 1, 0, 0, 0], fval=0, variables=op.variables, - status=OptimizationResultStatus.SUCCESS) + x=[1, 1, 0, 0, 0], + fval=0, + variables=op.variables, + status=OptimizationResultStatus.SUCCESS, + ) def test_to_quadratic_program(self): """Test to_quadratic_program""" @@ -55,9 +57,9 @@ def test_to_quadratic_program(self): self.assertEqual(len(lin), 1) self.assertEqual(lin[0].sense, Constraint.Sense.EQ) self.assertEqual(lin[0].rhs, -30) - self.assertEqual(lin[0].linear.to_dict(), { - i: -2*num for i, num in enumerate(self.num_set) - }) + self.assertEqual( + lin[0].linear.to_dict(), {i: -2 * num for i, num in enumerate(self.num_set)} + ) def test_interpret(self): """Test interpret""" diff --git a/test/applications/test_set_packing.py b/test/applications/test_set_packing.py index 78ef6ce9c..ab2ca5421 100644 --- a/test/applications/test_set_packing.py +++ b/test/applications/test_set_packing.py @@ -14,14 +14,13 @@ from test.optimization_test_case import QiskitOptimizationTestCase from qiskit_optimization import QuadraticProgram -from qiskit_optimization.algorithms import (OptimizationResult, - OptimizationResultStatus) +from qiskit_optimization.algorithms import OptimizationResult, OptimizationResultStatus from qiskit_optimization.applications.set_packing import SetPacking -from qiskit_optimization.problems import (Constraint, QuadraticObjective, VarType) +from qiskit_optimization.problems import Constraint, QuadraticObjective, VarType class TestSetPacking(QiskitOptimizationTestCase): - """ Test SetPacking class""" + """Test SetPacking class""" def setUp(self): super().setUp() @@ -31,8 +30,11 @@ def setUp(self): for _ in range(5): op.binary_var() self.result = OptimizationResult( - x=[0, 0, 1, 1, 1], fval=3, variables=op.variables, - status=OptimizationResultStatus.SUCCESS) + x=[0, 0, 1, 1, 1], + fval=3, + variables=op.variables, + status=OptimizationResultStatus.SUCCESS, + ) def test_to_quadratic_program(self): """Test to_quadratic_program""" @@ -56,9 +58,14 @@ def test_to_quadratic_program(self): for i, lin in enumerate(lin): self.assertEqual(lin.sense, Constraint.Sense.LE) self.assertEqual(lin.rhs, 1) - self.assertEqual(lin.linear.to_dict(), { - j: 1 for j, subset in enumerate(self.list_of_subsets) - if i+1 in subset}) + self.assertEqual( + lin.linear.to_dict(), + { + j: 1 + for j, subset in enumerate(self.list_of_subsets) + if i + 1 in subset + }, + ) def test_interpret(self): """Test interpret""" diff --git a/test/applications/test_stable_set.py b/test/applications/test_stable_set.py index 63575034b..23d297769 100644 --- a/test/applications/test_stable_set.py +++ b/test/applications/test_stable_set.py @@ -16,14 +16,13 @@ import networkx as nx from qiskit_optimization import QuadraticProgram -from qiskit_optimization.algorithms import (OptimizationResult, - OptimizationResultStatus) +from qiskit_optimization.algorithms import OptimizationResult, OptimizationResultStatus from qiskit_optimization.applications.stable_set import StableSet -from qiskit_optimization.problems import (Constraint, QuadraticObjective, VarType) +from qiskit_optimization.problems import Constraint, QuadraticObjective, VarType class TestStableSet(QiskitOptimizationTestCase): - """ Test StableSet class""" + """Test StableSet class""" def setUp(self): super().setUp() @@ -32,8 +31,11 @@ def setUp(self): for _ in range(5): op.binary_var() self.result = OptimizationResult( - x=[1, 1, 1, 1, 0], fval=4, variables=op.variables, - status=OptimizationResultStatus.SUCCESS) + x=[1, 1, 1, 1, 0], + fval=4, + variables=op.variables, + status=OptimizationResultStatus.SUCCESS, + ) def test_to_quadratic_program(self): """Test to_quadratic_program""" @@ -49,7 +51,9 @@ def test_to_quadratic_program(self): obj = op.objective self.assertEqual(obj.sense, QuadraticObjective.Sense.MAXIMIZE) self.assertEqual(obj.constant, 0) - self.assertDictEqual(obj.linear.to_dict(), {0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0}) + self.assertDictEqual( + obj.linear.to_dict(), {0: 1.0, 1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0} + ) # Test constraint lin = op.linear_constraints self.assertEqual(len(lin), len(self.graph.edges)) @@ -66,4 +70,6 @@ def test_interpret(self): def test_node_colors(self): """Test node_colors""" stable_set = StableSet(self.graph) - self.assertEqual(stable_set._node_colors(self.result), ['r', 'r', 'r', 'r', 'darkgrey']) + self.assertEqual( + stable_set._node_colors(self.result), ["r", "r", "r", "r", "darkgrey"] + ) diff --git a/test/applications/test_tsp.py b/test/applications/test_tsp.py index 9534fb3e2..5e378fa67 100644 --- a/test/applications/test_tsp.py +++ b/test/applications/test_tsp.py @@ -19,40 +19,48 @@ import networkx as nx from qiskit_optimization import QuadraticProgram -from qiskit_optimization.algorithms import (OptimizationResult, - OptimizationResultStatus) +from qiskit_optimization.algorithms import OptimizationResult, OptimizationResultStatus from qiskit_optimization.applications.tsp import Tsp -from qiskit_optimization.problems import (Constraint, QuadraticObjective, VarType) +from qiskit_optimization.problems import Constraint, QuadraticObjective, VarType class TestTsp(QiskitOptimizationTestCase): - """ Test Tsp class""" + """Test Tsp class""" def setUp(self): super().setUp() random.seed(123) low = 0 high = 100 - pos = {i: (random.randint(low, high), random.randint(low, high)) for i in range(4)} - self.graph = nx.random_geometric_graph(4, np.hypot(high-low, high-low)+1, pos=pos) + pos = { + i: (random.randint(low, high), random.randint(low, high)) for i in range(4) + } + self.graph = nx.random_geometric_graph( + 4, np.hypot(high - low, high - low) + 1, pos=pos + ) for w, v in self.graph.edges: - delta = [self.graph.nodes[w]['pos'][i] - self.graph.nodes[v]['pos'][i] - for i in range(2)] - self.graph.edges[w, v]['weight'] = np.rint(np.hypot(delta[0], delta[1])) + delta = [ + self.graph.nodes[w]["pos"][i] - self.graph.nodes[v]["pos"][i] + for i in range(2) + ] + self.graph.edges[w, v]["weight"] = np.rint(np.hypot(delta[0], delta[1])) op = QuadraticProgram() for i in range(16): op.binary_var() self.result = OptimizationResult( - x=[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], fval=272, variables=op.variables, - status=OptimizationResultStatus.SUCCESS) + x=[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], + fval=272, + variables=op.variables, + status=OptimizationResultStatus.SUCCESS, + ) def test_to_quadratic_program(self): """Test to_quadratic_program""" tsp = Tsp(self.graph) op = tsp.to_quadratic_program() # Test name - self.assertEqual(op.name, 'TSP') + self.assertEqual(op.name, "TSP") # Test variables self.assertEqual(op.get_num_vars(), 16) for var in op.variables: @@ -63,7 +71,9 @@ def test_to_quadratic_program(self): self.assertEqual(obj.constant, 0) self.assertDictEqual(obj.linear.to_dict(), {}) for edge, val in obj.quadratic.to_dict().items(): - self.assertEqual(val, self.graph.edges[edge[0]//4, edge[1]//4]['weight']) + self.assertEqual( + val, self.graph.edges[edge[0] // 4, edge[1] // 4]["weight"] + ) # Test constraint lin = op.linear_constraints @@ -71,11 +81,16 @@ def test_to_quadratic_program(self): for i in range(4): self.assertEqual(lin[i].sense, Constraint.Sense.EQ) self.assertEqual(lin[i].rhs, 1) - self.assertEqual(lin[i].linear.to_dict(), {4*i: 1, 4*i+1: 1, 4*i+2: 1, 4*i+3: 1}) + self.assertEqual( + lin[i].linear.to_dict(), + {4 * i: 1, 4 * i + 1: 1, 4 * i + 2: 1, 4 * i + 3: 1}, + ) for i in range(4): - self.assertEqual(lin[4+i].sense, Constraint.Sense.EQ) - self.assertEqual(lin[4+i].rhs, 1) - self.assertEqual(lin[4+i].linear.to_dict(), {i: 1, 4+i: 1, 8+i: 1, 12+i: 1}) + self.assertEqual(lin[4 + i].sense, Constraint.Sense.EQ) + self.assertEqual(lin[4 + i].rhs, 1) + self.assertEqual( + lin[4 + i].linear.to_dict(), {i: 1, 4 + i: 1, 8 + i: 1, 12 + i: 1} + ) def test_interpret(self): """Test interpret""" @@ -91,6 +106,6 @@ def test_create_random_instance(self): """Test create_random_instance""" tsp = Tsp.create_random_instance(n=3, seed=123) graph = tsp.graph - edge_weight = [graph.edges[edge]['weight'] for edge in graph.edges] + edge_weight = [graph.edges[edge]["weight"] for edge in graph.edges] expected_weight = [48, 91, 63] self.assertEqual(edge_weight, expected_weight) diff --git a/test/applications/test_vehicle_routing.py b/test/applications/test_vehicle_routing.py index 05654eb7b..05d048ad2 100644 --- a/test/applications/test_vehicle_routing.py +++ b/test/applications/test_vehicle_routing.py @@ -18,47 +18,59 @@ import numpy as np from qiskit_optimization import QuadraticProgram -from qiskit_optimization.algorithms import (OptimizationResult, - OptimizationResultStatus) -from qiskit_optimization.applications.vehicle_routing import \ - VehicleRouting -from qiskit_optimization.problems import (Constraint, QuadraticObjective, - VarType) +from qiskit_optimization.algorithms import OptimizationResult, OptimizationResultStatus +from qiskit_optimization.applications.vehicle_routing import VehicleRouting +from qiskit_optimization.problems import Constraint, QuadraticObjective, VarType class TestVehicleRouting(QiskitOptimizationTestCase): - """ Test VehicleRouting class""" + """Test VehicleRouting class""" def setUp(self): super().setUp() random.seed(600) low = 0 high = 100 - pos = {i: (random.randint(low, high), random.randint(low, high)) for i in range(4)} - self.graph = nx.random_geometric_graph(4, np.hypot(high-low, high-low)+1, pos=pos) + pos = { + i: (random.randint(low, high), random.randint(low, high)) for i in range(4) + } + self.graph = nx.random_geometric_graph( + 4, np.hypot(high - low, high - low) + 1, pos=pos + ) for w, v in self.graph.edges: - delta = [self.graph.nodes[w]['pos'][i] - self.graph.nodes[v]['pos'][i] - for i in range(2)] - self.graph.edges[w, v]['weight'] = np.rint(np.hypot(delta[0], delta[1])) + delta = [ + self.graph.nodes[w]["pos"][i] - self.graph.nodes[v]["pos"][i] + for i in range(2) + ] + self.graph.edges[w, v]["weight"] = np.rint(np.hypot(delta[0], delta[1])) op = QuadraticProgram() for i in range(12): op.binary_var() self.result = OptimizationResult( - x=[1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0], fval=184, variables=op.variables, - status=OptimizationResultStatus.SUCCESS) + x=[1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0], + fval=184, + variables=op.variables, + status=OptimizationResultStatus.SUCCESS, + ) self.result_d2 = OptimizationResult( - x=[1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1], fval=208.0, variables=op.variables, - status=OptimizationResultStatus.SUCCESS) + x=[1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1], + fval=208.0, + variables=op.variables, + status=OptimizationResultStatus.SUCCESS, + ) self.result_nv3 = OptimizationResult( - x=[1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0], fval=212.0, variables=op.variables, - status=OptimizationResultStatus.SUCCESS) + x=[1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0], + fval=212.0, + variables=op.variables, + status=OptimizationResultStatus.SUCCESS, + ) def test_to_quadratic_program(self): """Test to_quadratic_program""" vehicle_routing = VehicleRouting(self.graph) op = vehicle_routing.to_quadratic_program() # Test name - self.assertEqual(op.name, 'Vehicle routing') + self.assertEqual(op.name, "Vehicle routing") # Test variables self.assertEqual(op.get_num_vars(), 12) for var in op.variables: @@ -67,9 +79,23 @@ def test_to_quadratic_program(self): obj = op.objective self.assertEqual(obj.sense, QuadraticObjective.Sense.MINIMIZE) self.assertEqual(obj.constant, 0) - self.assertDictEqual(obj.linear.to_dict(), {0: 49.0, 1: 36.0, 2: 21.0, 3: 49.0, 4: 65.0, - 5: 67.0, 6: 36.0, 7: 65.0, 8: 29.0, 9: 21.0, - 10: 67.0, 11: 29.0}) + self.assertDictEqual( + obj.linear.to_dict(), + { + 0: 49.0, + 1: 36.0, + 2: 21.0, + 3: 49.0, + 4: 65.0, + 5: 67.0, + 6: 36.0, + 7: 65.0, + 8: 29.0, + 9: 21.0, + 10: 67.0, + 11: 29.0, + }, + ) self.assertEqual(obj.quadratic.to_dict(), {}) # Test constraint lin = op.linear_constraints @@ -77,7 +103,10 @@ def test_to_quadratic_program(self): for i in range(3): self.assertEqual(lin[i].sense, Constraint.Sense.EQ) self.assertEqual(lin[i].rhs, 1) - self.assertEqual(lin[i].linear.to_dict(), {3*(i+1): 1, 3*(i+1)+1: 1, 3*(i+1)+2: 1}) + self.assertEqual( + lin[i].linear.to_dict(), + {3 * (i + 1): 1, 3 * (i + 1) + 1: 1, 3 * (i + 1) + 2: 1}, + ) self.assertEqual(lin[3].sense, Constraint.Sense.EQ) self.assertEqual(lin[3].rhs, 1) self.assertEqual(lin[3].linear.to_dict(), {0: 1, 7: 1, 10: 1}) @@ -104,32 +133,40 @@ def test_to_quadratic_program(self): self.assertEqual(lin[10].linear.to_dict(), {8: 1.0, 11: 1.0}) self.assertEqual(lin[11].sense, Constraint.Sense.LE) self.assertEqual(lin[11].rhs, 2) - self.assertEqual(lin[11].linear.to_dict(), {4: 1, 5: 1, 7: 1, 8: 1, 10: 1, 11: 1}) + self.assertEqual( + lin[11].linear.to_dict(), {4: 1, 5: 1, 7: 1, 8: 1, 10: 1, 11: 1} + ) def test_interpret(self): """Test interpret""" vehicle_routing = VehicleRouting(self.graph) - self.assertEqual(vehicle_routing.interpret(self.result), - [[[0, 1], [1, 0]], [[0, 2], [2, 3], [3, 0]]]) + self.assertEqual( + vehicle_routing.interpret(self.result), + [[[0, 1], [1, 0]], [[0, 2], [2, 3], [3, 0]]], + ) def test_edgelist(self): """Test _edgelist""" vehicle_routing = VehicleRouting(self.graph) - self.assertEqual(vehicle_routing._edgelist(vehicle_routing.interpret(self.result)), - [[0, 1], [1, 0], [0, 2], [2, 3], [3, 0]]) + self.assertEqual( + vehicle_routing._edgelist(vehicle_routing.interpret(self.result)), + [[0, 1], [1, 0], [0, 2], [2, 3], [3, 0]], + ) def test_edge_color(self): """Test _edge_color""" vehicle_routing = VehicleRouting(self.graph) - self.assertEqual(vehicle_routing._edge_color(vehicle_routing.interpret(self.result)), - [0.0, 0.0, 0.5, 0.5, 0.5]) + self.assertEqual( + vehicle_routing._edge_color(vehicle_routing.interpret(self.result)), + [0.0, 0.0, 0.5, 0.5, 0.5], + ) def test_to_quadratic_program_d2(self): """Test to_quadratic_program for depot=2""" vehicle_routing = VehicleRouting(self.graph, depot=2) op = vehicle_routing.to_quadratic_program() # Test name - self.assertEqual(op.name, 'Vehicle routing') + self.assertEqual(op.name, "Vehicle routing") # Test variables self.assertEqual(op.get_num_vars(), 12) for var in op.variables: @@ -138,9 +175,23 @@ def test_to_quadratic_program_d2(self): obj = op.objective self.assertEqual(obj.sense, QuadraticObjective.Sense.MINIMIZE) self.assertEqual(obj.constant, 0) - self.assertDictEqual(obj.linear.to_dict(), {0: 49.0, 1: 36.0, 2: 21.0, 3: 49.0, 4: 65.0, - 5: 67.0, 6: 36.0, 7: 65.0, 8: 29.0, 9: 21.0, - 10: 67.0, 11: 29.0}) + self.assertDictEqual( + obj.linear.to_dict(), + { + 0: 49.0, + 1: 36.0, + 2: 21.0, + 3: 49.0, + 4: 65.0, + 5: 67.0, + 6: 36.0, + 7: 65.0, + 8: 29.0, + 9: 21.0, + 10: 67.0, + 11: 29.0, + }, + ) self.assertEqual(obj.quadratic.to_dict(), {}) # Test constraint lin = op.linear_constraints @@ -150,7 +201,10 @@ def test_to_quadratic_program_d2(self): j = c012[i] self.assertEqual(lin[i].sense, Constraint.Sense.EQ) self.assertEqual(lin[i].rhs, 1) - self.assertEqual(lin[i].linear.to_dict(), {3*(j+1): 1, 3*(j+1)+1: 1, 3*(j+1)+2: 1}) + self.assertEqual( + lin[i].linear.to_dict(), + {3 * (j + 1): 1, 3 * (j + 1) + 1: 1, 3 * (j + 1) + 2: 1}, + ) self.assertEqual(lin[3].sense, Constraint.Sense.EQ) self.assertEqual(lin[3].rhs, 1) self.assertEqual(lin[3].linear.to_dict(), {3: 1, 6: 1, 9: 1}) @@ -177,32 +231,40 @@ def test_to_quadratic_program_d2(self): self.assertEqual(lin[10].linear.to_dict(), {5: 1.0, 10: 1.0}) self.assertEqual(lin[11].sense, Constraint.Sense.LE) self.assertEqual(lin[11].rhs, 2) - self.assertEqual(lin[11].linear.to_dict(), {0: 1, 2: 1, 3: 1, 5: 1, 9: 1, 10: 1}) + self.assertEqual( + lin[11].linear.to_dict(), {0: 1, 2: 1, 3: 1, 5: 1, 9: 1, 10: 1} + ) def test_interpret_d2(self): """Test interpret for depot=2""" vehicle_routing = VehicleRouting(self.graph, depot=2) - self.assertEqual(vehicle_routing.interpret(self.result_d2), - [[[2, 0], [0, 1], [1, 2]], [[2, 3], [3, 2]]]) + self.assertEqual( + vehicle_routing.interpret(self.result_d2), + [[[2, 0], [0, 1], [1, 2]], [[2, 3], [3, 2]]], + ) def test_edgelist_d2(self): """Test _edgelist for depot=2""" vehicle_routing = VehicleRouting(self.graph, depot=2) - self.assertEqual(vehicle_routing._edgelist(vehicle_routing.interpret(self.result_d2)), - [[2, 0], [0, 1], [1, 2], [2, 3], [3, 2]]) + self.assertEqual( + vehicle_routing._edgelist(vehicle_routing.interpret(self.result_d2)), + [[2, 0], [0, 1], [1, 2], [2, 3], [3, 2]], + ) def test_edge_color_d2(self): """Test _edge_color for depot=2""" vehicle_routing = VehicleRouting(self.graph, depot=2) - self.assertEqual(vehicle_routing._edge_color(vehicle_routing.interpret(self.result_d2)), - [0.0, 0.0, 0.0, 0.5, 0.5]) + self.assertEqual( + vehicle_routing._edge_color(vehicle_routing.interpret(self.result_d2)), + [0.0, 0.0, 0.0, 0.5, 0.5], + ) def test_to_quadratic_program_nv3(self): """Test to_quadratic_program for num_vehicles=3""" vehicle_routing = VehicleRouting(self.graph, num_vehicles=3) op = vehicle_routing.to_quadratic_program() # Test name - self.assertEqual(op.name, 'Vehicle routing') + self.assertEqual(op.name, "Vehicle routing") # Test variables self.assertEqual(op.get_num_vars(), 12) for var in op.variables: @@ -211,9 +273,23 @@ def test_to_quadratic_program_nv3(self): obj = op.objective self.assertEqual(obj.sense, QuadraticObjective.Sense.MINIMIZE) self.assertEqual(obj.constant, 0) - self.assertDictEqual(obj.linear.to_dict(), {0: 49.0, 1: 36.0, 2: 21.0, 3: 49.0, 4: 65.0, - 5: 67.0, 6: 36.0, 7: 65.0, 8: 29.0, 9: 21.0, - 10: 67.0, 11: 29.0}) + self.assertDictEqual( + obj.linear.to_dict(), + { + 0: 49.0, + 1: 36.0, + 2: 21.0, + 3: 49.0, + 4: 65.0, + 5: 67.0, + 6: 36.0, + 7: 65.0, + 8: 29.0, + 9: 21.0, + 10: 67.0, + 11: 29.0, + }, + ) self.assertEqual(obj.quadratic.to_dict(), {}) # Test constraint lin = op.linear_constraints @@ -221,7 +297,10 @@ def test_to_quadratic_program_nv3(self): for i in range(3): self.assertEqual(lin[i].sense, Constraint.Sense.EQ) self.assertEqual(lin[i].rhs, 1) - self.assertEqual(lin[i].linear.to_dict(), {3*(i+1): 1, 3*(i+1)+1: 1, 3*(i+1)+2: 1}) + self.assertEqual( + lin[i].linear.to_dict(), + {3 * (i + 1): 1, 3 * (i + 1) + 1: 1, 3 * (i + 1) + 2: 1}, + ) self.assertEqual(lin[3].sense, Constraint.Sense.EQ) self.assertEqual(lin[3].rhs, 1) self.assertEqual(lin[3].linear.to_dict(), {0: 1, 7: 1, 10: 1}) @@ -248,34 +327,44 @@ def test_to_quadratic_program_nv3(self): self.assertEqual(lin[10].linear.to_dict(), {8: 1.0, 11: 1.0}) self.assertEqual(lin[11].sense, Constraint.Sense.LE) self.assertEqual(lin[11].rhs, 2) - self.assertEqual(lin[11].linear.to_dict(), {4: 1, 5: 1, 7: 1, 8: 1, 10: 1, 11: 1}) + self.assertEqual( + lin[11].linear.to_dict(), {4: 1, 5: 1, 7: 1, 8: 1, 10: 1, 11: 1} + ) def test_interpret_nv3(self): """Test interpret for num_vehicles=3""" vehicle_routing = VehicleRouting(self.graph, num_vehicles=3) - self.assertEqual(vehicle_routing.interpret(self.result_nv3), - [[[0, 1], [1, 0]], [[0, 2], [2, 0]], [[0, 3], [3, 0]]]) + self.assertEqual( + vehicle_routing.interpret(self.result_nv3), + [[[0, 1], [1, 0]], [[0, 2], [2, 0]], [[0, 3], [3, 0]]], + ) def test_edgelist_nv3(self): """Test _edgelist for num_vehicles=3""" vehicle_routing = VehicleRouting(self.graph, num_vehicles=3) - self.assertEqual(vehicle_routing._edgelist(vehicle_routing.interpret(self.result_nv3)), - [[0, 1], [1, 0], [0, 2], [2, 0], [0, 3], [3, 0]]) + self.assertEqual( + vehicle_routing._edgelist(vehicle_routing.interpret(self.result_nv3)), + [[0, 1], [1, 0], [0, 2], [2, 0], [0, 3], [3, 0]], + ) def test_edge_color_nv3(self): """Test _edge_color for num_vehicles=3""" vehicle_routing = VehicleRouting(self.graph, num_vehicles=3) - self.assertEqual(vehicle_routing._edge_color(vehicle_routing.interpret(self.result_nv3)), - [0.0, 0.0, 1/3, 1/3, 2/3, 2/3]) + self.assertEqual( + vehicle_routing._edge_color(vehicle_routing.interpret(self.result_nv3)), + [0.0, 0.0, 1 / 3, 1 / 3, 2 / 3, 2 / 3], + ) def test_create_random_instance(self): """Test create_random_instance""" vehicle_routing = VehicleRouting.create_random_instance(n=4, seed=600) graph = vehicle_routing.graph for node in graph.nodes: - self.assertEqual(graph.nodes[node]['pos'], self.graph.nodes[node]['pos']) + self.assertEqual(graph.nodes[node]["pos"], self.graph.nodes[node]["pos"]) for edge in graph.edges: - self.assertEqual(graph.edges[edge]['weight'], self.graph.edges[edge]['weight']) + self.assertEqual( + graph.edges[edge]["weight"], self.graph.edges[edge]["weight"] + ) def test_num_vehicles(self): """Test num_vehicles""" diff --git a/test/applications/test_vertex_cover.py b/test/applications/test_vertex_cover.py index fdd801c0c..fbc34455d 100644 --- a/test/applications/test_vertex_cover.py +++ b/test/applications/test_vertex_cover.py @@ -16,15 +16,13 @@ import networkx as nx from qiskit_optimization import QuadraticProgram -from qiskit_optimization.algorithms import (OptimizationResult, - OptimizationResultStatus) +from qiskit_optimization.algorithms import OptimizationResult, OptimizationResultStatus from qiskit_optimization.applications.vertex_cover import VertexCover -from qiskit_optimization.problems import (Constraint, QuadraticObjective, - VarType) +from qiskit_optimization.problems import Constraint, QuadraticObjective, VarType class TestVertexCover(QiskitOptimizationTestCase): - """ Test VertexCover class""" + """Test VertexCover class""" def setUp(self): """set up the test class""" @@ -34,8 +32,11 @@ def setUp(self): for _ in range(5): op.binary_var() self.result = OptimizationResult( - x=[0, 0, 0, 0, 1], fval=1, variables=op.variables, - status=OptimizationResultStatus.SUCCESS) + x=[0, 0, 0, 0, 1], + fval=1, + variables=op.variables, + status=OptimizationResultStatus.SUCCESS, + ) def test_to_quadratic_program(self): """Test to_quadratic_program""" @@ -69,9 +70,11 @@ def test_interpret(self): def test_node_colors(self): """Test _node_colors""" vertex_cover = VertexCover(self.graph) - self.assertEqual(vertex_cover._node_colors(self.result), - ['darkgrey', 'darkgrey', 'darkgrey', 'darkgrey', 'r']) + self.assertEqual( + vertex_cover._node_colors(self.result), + ["darkgrey", "darkgrey", "darkgrey", "darkgrey", "r"], + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/converters/test_converters.py b/test/converters/test_converters.py index 6a8dc29ef..32c93dad3 100644 --- a/test/converters/test_converters.py +++ b/test/converters/test_converters.py @@ -14,32 +14,42 @@ import logging import unittest -from test.optimization_test_case import QiskitOptimizationTestCase, requires_extra_library +from test.optimization_test_case import ( + QiskitOptimizationTestCase, + requires_extra_library, +) import numpy as np from docplex.mp.model import Model from qiskit.algorithms import NumPyMinimumEigensolver from qiskit.opflow import Z, I from qiskit_optimization import QuadraticProgram, QiskitOptimizationError -from qiskit_optimization.algorithms import MinimumEigenOptimizer, CplexOptimizer, ADMMOptimizer +from qiskit_optimization.algorithms import ( + MinimumEigenOptimizer, + CplexOptimizer, + ADMMOptimizer, +) from qiskit_optimization.algorithms.admm_optimizer import ADMMParameters -from qiskit_optimization.converters import (InequalityToEquality, IntegerToBinary, - LinearEqualityToPenalty) +from qiskit_optimization.converters import ( + InequalityToEquality, + IntegerToBinary, + LinearEqualityToPenalty, +) from qiskit_optimization.problems import Constraint, Variable logger = logging.getLogger(__name__) QUBIT_OP_MAXIMIZE_SAMPLE = ( - -199999.5 * (I ^ I ^ I ^ Z) - + -399999.5 * (I ^ I ^ Z ^ I) - + -599999.5 * (I ^ Z ^ I ^ I) - + -799999.5 * (Z ^ I ^ I ^ I) - + 100000 * (I ^ I ^ Z ^ Z) - + 150000 * (I ^ Z ^ I ^ Z) - + 300000 * (I ^ Z ^ Z ^ I) - + 200000 * (Z ^ I ^ I ^ Z) - + 400000 * (Z ^ I ^ Z ^ I) - + 600000 * (Z ^ Z ^ I ^ I) + -199999.5 * (I ^ I ^ I ^ Z) + + -399999.5 * (I ^ I ^ Z ^ I) + + -599999.5 * (I ^ Z ^ I ^ I) + + -799999.5 * (Z ^ I ^ I ^ I) + + 100000 * (I ^ I ^ Z ^ Z) + + 150000 * (I ^ Z ^ I ^ Z) + + 300000 * (I ^ Z ^ Z ^ I) + + 200000 * (Z ^ I ^ I ^ Z) + + 400000 * (Z ^ I ^ Z ^ I) + + 600000 * (Z ^ Z ^ I ^ I) ) OFFSET_MAXIMIZE_SAMPLE = 1149998 @@ -48,7 +58,7 @@ class TestConverters(QiskitOptimizationTestCase): """Test Converters""" def test_empty_problem(self): - """ Test empty problem """ + """Test empty problem""" op = QuadraticProgram() conv = InequalityToEquality() op = conv.convert(op) @@ -73,28 +83,37 @@ def test_valid_variable_type(self): _ = op.to_ising() def test_inequality_binary(self): - """ Test InequalityToEqualityConverter with binary variables """ + """Test InequalityToEqualityConverter with binary variables""" op = QuadraticProgram() for i in range(3): - op.binary_var(name='x{}'.format(i)) + op.binary_var(name="x{}".format(i)) # Linear constraints - linear_constraint = {'x0': 1, 'x1': 1} - op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, 'x0x1') - linear_constraint = {'x1': 1, 'x2': -1} - op.linear_constraint(linear_constraint, Constraint.Sense.LE, 2, 'x1x2') - linear_constraint = {'x0': 1, 'x2': 3} - op.linear_constraint(linear_constraint, Constraint.Sense.GE, 2, 'x0x2') + linear_constraint = {"x0": 1, "x1": 1} + op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, "x0x1") + linear_constraint = {"x1": 1, "x2": -1} + op.linear_constraint(linear_constraint, Constraint.Sense.LE, 2, "x1x2") + linear_constraint = {"x0": 1, "x2": 3} + op.linear_constraint(linear_constraint, Constraint.Sense.GE, 2, "x0x2") # Quadratic constraints - quadratic = {('x0', 'x1'): 1, ('x1', 'x2'): 2} - op.quadratic_constraint({}, quadratic, Constraint.Sense.LE, 3, 'x0x1_x1x2LE') - quadratic = {('x0', 'x1'): 3, ('x1', 'x2'): 4} - op.quadratic_constraint({}, quadratic, Constraint.Sense.GE, 3, 'x0x1_x1x2GE') + quadratic = {("x0", "x1"): 1, ("x1", "x2"): 2} + op.quadratic_constraint({}, quadratic, Constraint.Sense.LE, 3, "x0x1_x1x2LE") + quadratic = {("x0", "x1"): 3, ("x1", "x2"): 4} + op.quadratic_constraint({}, quadratic, Constraint.Sense.GE, 3, "x0x1_x1x2GE") # Convert inequality constraints into equality constraints conv = InequalityToEquality() op2 = conv.convert(op) - self.assertListEqual([v.name for v in op2.variables], - ['x0', 'x1', 'x2', 'x1x2@int_slack', 'x0x2@int_slack', - 'x0x1_x1x2LE@int_slack', 'x0x1_x1x2GE@int_slack']) + self.assertListEqual( + [v.name for v in op2.variables], + [ + "x0", + "x1", + "x2", + "x1x2@int_slack", + "x0x2@int_slack", + "x0x1_x1x2LE@int_slack", + "x0x1_x1x2GE@int_slack", + ], + ) # Check names and objective senses self.assertEqual(op.name, op2.name) self.assertEqual(op.objective.sense, op2.objective.sense) @@ -145,27 +164,36 @@ def test_inequality_binary(self): np.testing.assert_array_almost_equal(new_x, np.arange(3)) def test_inequality_integer(self): - """ Test InequalityToEqualityConverter with integer variables """ + """Test InequalityToEqualityConverter with integer variables""" op = QuadraticProgram() for i in range(3): - op.integer_var(name='x{}'.format(i), lowerbound=-3, upperbound=3) + op.integer_var(name="x{}".format(i), lowerbound=-3, upperbound=3) # Linear constraints - linear_constraint = {'x0': 1, 'x1': 1} - op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, 'x0x1') - linear_constraint = {'x1': 1, 'x2': -1} - op.linear_constraint(linear_constraint, Constraint.Sense.LE, 2, 'x1x2') - linear_constraint = {'x0': 1, 'x2': 3} - op.linear_constraint(linear_constraint, Constraint.Sense.GE, 2, 'x0x2') + linear_constraint = {"x0": 1, "x1": 1} + op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, "x0x1") + linear_constraint = {"x1": 1, "x2": -1} + op.linear_constraint(linear_constraint, Constraint.Sense.LE, 2, "x1x2") + linear_constraint = {"x0": 1, "x2": 3} + op.linear_constraint(linear_constraint, Constraint.Sense.GE, 2, "x0x2") # Quadratic constraints - quadratic = {('x0', 'x1'): 1, ('x1', 'x2'): 2} - op.quadratic_constraint({}, quadratic, Constraint.Sense.LE, 3, 'x0x1_x1x2LE') - quadratic = {('x0', 'x1'): 3, ('x1', 'x2'): 4} - op.quadratic_constraint({}, quadratic, Constraint.Sense.GE, 3, 'x0x1_x1x2GE') + quadratic = {("x0", "x1"): 1, ("x1", "x2"): 2} + op.quadratic_constraint({}, quadratic, Constraint.Sense.LE, 3, "x0x1_x1x2LE") + quadratic = {("x0", "x1"): 3, ("x1", "x2"): 4} + op.quadratic_constraint({}, quadratic, Constraint.Sense.GE, 3, "x0x1_x1x2GE") conv = InequalityToEquality() op2 = conv.convert(op) - self.assertListEqual([v.name for v in op2.variables], - ['x0', 'x1', 'x2', 'x1x2@int_slack', 'x0x2@int_slack', - 'x0x1_x1x2LE@int_slack', 'x0x1_x1x2GE@int_slack']) + self.assertListEqual( + [v.name for v in op2.variables], + [ + "x0", + "x1", + "x2", + "x1x2@int_slack", + "x0x2@int_slack", + "x0x1_x1x2LE@int_slack", + "x0x1_x1x2GE@int_slack", + ], + ) # For linear constraints lst = [ op2.linear_constraints[0].linear.to_dict()[0], @@ -213,85 +241,85 @@ def test_inequality_integer(self): np.testing.assert_array_almost_equal(new_x, np.arange(3)) def test_inequality_mode_integer(self): - """ Test integer mode of InequalityToEqualityConverter() """ + """Test integer mode of InequalityToEqualityConverter()""" op = QuadraticProgram() for i in range(3): - op.binary_var(name='x{}'.format(i)) + op.binary_var(name="x{}".format(i)) # Linear constraints - linear_constraint = {'x0': 1, 'x1': 1} - op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, 'x0x1') - linear_constraint = {'x1': 1, 'x2': -1} - op.linear_constraint(linear_constraint, Constraint.Sense.LE, 2, 'x1x2') - linear_constraint = {'x0': 1, 'x2': 3} - op.linear_constraint(linear_constraint, Constraint.Sense.GE, 2, 'x0x2') - conv = InequalityToEquality(mode='integer') + linear_constraint = {"x0": 1, "x1": 1} + op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, "x0x1") + linear_constraint = {"x1": 1, "x2": -1} + op.linear_constraint(linear_constraint, Constraint.Sense.LE, 2, "x1x2") + linear_constraint = {"x0": 1, "x2": 3} + op.linear_constraint(linear_constraint, Constraint.Sense.GE, 2, "x0x2") + conv = InequalityToEquality(mode="integer") op2 = conv.convert(op) lst = [op2.variables[3].vartype, op2.variables[4].vartype] self.assertListEqual(lst, [Variable.Type.INTEGER, Variable.Type.INTEGER]) def test_inequality_mode_continuous(self): - """ Test continuous mode of InequalityToEqualityConverter() """ + """Test continuous mode of InequalityToEqualityConverter()""" op = QuadraticProgram() for i in range(3): - op.binary_var(name='x{}'.format(i)) + op.binary_var(name="x{}".format(i)) # Linear constraints - linear_constraint = {'x0': 1, 'x1': 1} - op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, 'x0x1') - linear_constraint = {'x1': 1, 'x2': -1} - op.linear_constraint(linear_constraint, Constraint.Sense.LE, 2, 'x1x2') - linear_constraint = {'x0': 1, 'x2': 3} - op.linear_constraint(linear_constraint, Constraint.Sense.GE, 2, 'x0x2') - conv = InequalityToEquality(mode='continuous') + linear_constraint = {"x0": 1, "x1": 1} + op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, "x0x1") + linear_constraint = {"x1": 1, "x2": -1} + op.linear_constraint(linear_constraint, Constraint.Sense.LE, 2, "x1x2") + linear_constraint = {"x0": 1, "x2": 3} + op.linear_constraint(linear_constraint, Constraint.Sense.GE, 2, "x0x2") + conv = InequalityToEquality(mode="continuous") op2 = conv.convert(op) lst = [op2.variables[3].vartype, op2.variables[4].vartype] self.assertListEqual(lst, [Variable.Type.CONTINUOUS, Variable.Type.CONTINUOUS]) def test_inequality_mode_auto(self): - """ Test auto mode of InequalityToEqualityConverter() """ + """Test auto mode of InequalityToEqualityConverter()""" op = QuadraticProgram() for i in range(3): - op.binary_var(name='x{}'.format(i)) + op.binary_var(name="x{}".format(i)) # Linear constraints - linear_constraint = {'x0': 1, 'x1': 1} - op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, 'x0x1') - linear_constraint = {'x1': 1, 'x2': -1} - op.linear_constraint(linear_constraint, Constraint.Sense.LE, 2, 'x1x2') - linear_constraint = {'x0': 1.1, 'x2': 2.2} - op.linear_constraint(linear_constraint, Constraint.Sense.GE, 3.3, 'x0x2') - conv = InequalityToEquality(mode='auto') + linear_constraint = {"x0": 1, "x1": 1} + op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, "x0x1") + linear_constraint = {"x1": 1, "x2": -1} + op.linear_constraint(linear_constraint, Constraint.Sense.LE, 2, "x1x2") + linear_constraint = {"x0": 1.1, "x2": 2.2} + op.linear_constraint(linear_constraint, Constraint.Sense.GE, 3.3, "x0x2") + conv = InequalityToEquality(mode="auto") op2 = conv.convert(op) lst = [op2.variables[3].vartype, op2.variables[4].vartype] self.assertListEqual(lst, [Variable.Type.INTEGER, Variable.Type.CONTINUOUS]) def test_penalize_sense(self): - """ Test PenalizeLinearEqualityConstraints with senses """ + """Test PenalizeLinearEqualityConstraints with senses""" op = QuadraticProgram() for i in range(3): - op.binary_var(name='x{}'.format(i)) + op.binary_var(name="x{}".format(i)) # Linear constraints - linear_constraint = {'x0': 1, 'x1': 1} - op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, 'x0x1') - linear_constraint = {'x1': 1, 'x2': -1} - op.linear_constraint(linear_constraint, Constraint.Sense.LE, 2, 'x1x2') - linear_constraint = {'x0': 1, 'x2': 3} - op.linear_constraint(linear_constraint, Constraint.Sense.GE, 2, 'x0x2') + linear_constraint = {"x0": 1, "x1": 1} + op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, "x0x1") + linear_constraint = {"x1": 1, "x2": -1} + op.linear_constraint(linear_constraint, Constraint.Sense.LE, 2, "x1x2") + linear_constraint = {"x0": 1, "x2": 3} + op.linear_constraint(linear_constraint, Constraint.Sense.GE, 2, "x0x2") self.assertEqual(op.get_num_linear_constraints(), 3) conv = LinearEqualityToPenalty() with self.assertRaises(QiskitOptimizationError): conv.convert(op) def test_penalize_binary(self): - """ Test PenalizeLinearEqualityConstraints with binary variables """ + """Test PenalizeLinearEqualityConstraints with binary variables""" op = QuadraticProgram() for i in range(3): - op.binary_var(name='x{}'.format(i)) + op.binary_var(name="x{}".format(i)) # Linear constraints - linear_constraint = {'x0': 1, 'x1': 1} - op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, 'x0x1') - linear_constraint = {'x1': 1, 'x2': -1} - op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 2, 'x1x2') - linear_constraint = {'x0': 1, 'x2': 3} - op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 2, 'x0x2') + linear_constraint = {"x0": 1, "x1": 1} + op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, "x0x1") + linear_constraint = {"x1": 1, "x2": -1} + op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 2, "x1x2") + linear_constraint = {"x0": 1, "x2": 3} + op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 2, "x0x2") self.assertEqual(op.get_num_linear_constraints(), 3) conv = LinearEqualityToPenalty() op2 = conv.convert(op) @@ -301,18 +329,18 @@ def test_penalize_binary(self): np.testing.assert_array_almost_equal(new_x, np.arange(3)) def test_penalize_integer(self): - """ Test PenalizeLinearEqualityConstraints with integer variables """ + """Test PenalizeLinearEqualityConstraints with integer variables""" op = QuadraticProgram() for i in range(3): - op.integer_var(name='x{}'.format(i), lowerbound=-3, upperbound=3) + op.integer_var(name="x{}".format(i), lowerbound=-3, upperbound=3) # Linear constraints - linear_constraint = {'x0': 1, 'x1': 1} - op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, 'x0x1') - linear_constraint = {'x1': 1, 'x2': -1} - op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 2, 'x1x2') - linear_constraint = {'x0': 1, 'x2': -1} - op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, 'x0x2') - op.minimize(constant=3, linear={'x0': 1}, quadratic={('x1', 'x2'): 2}) + linear_constraint = {"x0": 1, "x1": 1} + op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, "x0x1") + linear_constraint = {"x1": 1, "x2": -1} + op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 2, "x1x2") + linear_constraint = {"x0": 1, "x2": -1} + op.linear_constraint(linear_constraint, Constraint.Sense.EQ, 1, "x0x2") + op.minimize(constant=3, linear={"x0": 1}, quadratic={("x1", "x2"): 2}) self.assertEqual(op.get_num_linear_constraints(), 3) conv = LinearEqualityToPenalty() op2 = conv.convert(op) @@ -322,11 +350,11 @@ def test_penalize_integer(self): np.testing.assert_array_almost_equal(new_x, [0, 1, -1]) def test_integer_to_binary(self): - """ Test integer to binary """ + """Test integer to binary""" op = QuadraticProgram() for i in range(0, 2): - op.binary_var(name='x{}'.format(i)) - op.integer_var(name='x2', lowerbound=0, upperbound=5) + op.binary_var(name="x{}".format(i)) + op.integer_var(name="x2", lowerbound=0, upperbound=5) linear = {} for i, x in enumerate(op.variables): linear[x.name] = i + 1 @@ -334,35 +362,39 @@ def test_integer_to_binary(self): conv = IntegerToBinary() op2 = conv.convert(op) self.assertEqual(op2.get_num_vars(), 5) - self.assertListEqual([x.vartype for x in op2.variables], [Variable.Type.BINARY] * 5) - self.assertListEqual([x.name for x in op2.variables], ['x0', 'x1', 'x2@0', 'x2@1', 'x2@2']) + self.assertListEqual( + [x.vartype for x in op2.variables], [Variable.Type.BINARY] * 5 + ) + self.assertListEqual( + [x.name for x in op2.variables], ["x0", "x1", "x2@0", "x2@1", "x2@2"] + ) dct = op2.objective.linear.to_dict() self.assertEqual(dct[2], 3) self.assertEqual(dct[3], 6) self.assertEqual(dct[4], 6) def test_binary_to_integer(self): - """ Test binary to integer """ + """Test binary to integer""" op = QuadraticProgram() for i in range(0, 2): - op.binary_var(name='x{}'.format(i)) - op.integer_var(name='x2', lowerbound=0, upperbound=5) - linear = {'x0': 1, 'x1': 2, 'x2': 1} + op.binary_var(name="x{}".format(i)) + op.integer_var(name="x2", lowerbound=0, upperbound=5) + linear = {"x0": 1, "x1": 2, "x2": 1} op.maximize(0, linear, {}) linear = {} for x in op.variables: linear[x.name] = 1 - op.linear_constraint(linear, Constraint.Sense.EQ, 6, 'x0x1x2') + op.linear_constraint(linear, Constraint.Sense.EQ, 6, "x0x1x2") conv = IntegerToBinary() _ = conv.convert(op) new_x = conv.interpret([0, 1, 1, 1, 1]) np.testing.assert_array_almost_equal(new_x, [0, 1, 5]) def test_optimizationproblem_to_ising(self): - """ Test optimization problem to operators""" + """Test optimization problem to operators""" op = QuadraticProgram() for i in range(4): - op.binary_var(name='x{}'.format(i)) + op.binary_var(name="x{}".format(i)) linear = {} for x in op.variables: linear[x.name] = 1 @@ -370,7 +402,7 @@ def test_optimizationproblem_to_ising(self): linear = {} for i, x in enumerate(op.variables): linear[x.name] = i + 1 - op.linear_constraint(linear, Constraint.Sense.EQ, 3, 'sum1') + op.linear_constraint(linear, Constraint.Sense.EQ, 3, "sum1") penalize = LinearEqualityToPenalty(penalty=1e5) op2 = penalize.convert(op) qubitop, offset = op2.to_ising() @@ -378,7 +410,7 @@ def test_optimizationproblem_to_ising(self): self.assertEqual(offset, OFFSET_MAXIMIZE_SAMPLE) def test_ising_to_quadraticprogram_linear(self): - """ Test optimization problem to operators with linear=True""" + """Test optimization problem to operators with linear=True""" op = QUBIT_OP_MAXIMIZE_SAMPLE offset = OFFSET_MAXIMIZE_SAMPLE @@ -413,7 +445,7 @@ def test_ising_to_quadraticprogram_linear(self): ) def test_ising_to_quadraticprogram_quadratic(self): - """ Test optimization problem to operators with linear=False""" + """Test optimization problem to operators with linear=False""" op = QUBIT_OP_MAXIMIZE_SAMPLE offset = OFFSET_MAXIMIZE_SAMPLE @@ -444,10 +476,10 @@ def test_ising_to_quadraticprogram_quadratic(self): @requires_extra_library def test_continuous_variable_decode(self): - """ Test decode func of IntegerToBinaryConverter for continuous variables""" - mdl = Model('test_continuous_varable_decode') - c = mdl.continuous_var(lb=0, ub=10.9, name='c') - x = mdl.binary_var(name='x') + """Test decode func of IntegerToBinaryConverter for continuous variables""" + mdl = Model("test_continuous_varable_decode") + c = mdl.continuous_var(lb=0, ub=10.9, name="c") + x = mdl.binary_var(name="x") mdl.maximize(c + x * x) op = QuadraticProgram() op.from_docplex(mdl) @@ -466,13 +498,15 @@ def test_continuous_variable_decode(self): self.assertEqual(new_x[0], 10.9) def test_auto_penalty(self): - """ Test auto penalty function""" + """Test auto penalty function""" op = QuadraticProgram() - op.binary_var('x') - op.binary_var('y') - op.binary_var('z') - op.minimize(constant=3, linear={'x': 1}, quadratic={('x', 'y'): 2}) - op.linear_constraint(linear={'x': 1, 'y': 1, 'z': 1}, sense='EQ', rhs=2, name='xyz_eq') + op.binary_var("x") + op.binary_var("y") + op.binary_var("z") + op.minimize(constant=3, linear={"x": 1}, quadratic={("x", "y"): 2}) + op.linear_constraint( + linear={"x": 1, "y": 1, "z": 1}, sense="EQ", rhs=2, name="xyz_eq" + ) lineq2penalty = LinearEqualityToPenalty(penalty=1e5) lineq2penalty_auto = LinearEqualityToPenalty() qubo = lineq2penalty.convert(op) @@ -485,32 +519,36 @@ def test_auto_penalty(self): np.testing.assert_array_almost_equal(result.x, result_auto.x) def test_auto_penalty_warning(self): - """ Test warnings of auto penalty function""" + """Test warnings of auto penalty function""" op = QuadraticProgram() - op.binary_var('x') - op.binary_var('y') - op.binary_var('z') - op.minimize(linear={'x': 1, 'y': 2}) - op.linear_constraint(linear={'x': 0.5, 'y': 0.5, 'z': 0.5}, sense='EQ', rhs=1, name='xyz') - with self.assertLogs('qiskit_optimization', level='WARNING') as log: + op.binary_var("x") + op.binary_var("y") + op.binary_var("z") + op.minimize(linear={"x": 1, "y": 2}) + op.linear_constraint( + linear={"x": 0.5, "y": 0.5, "z": 0.5}, sense="EQ", rhs=1, name="xyz" + ) + with self.assertLogs("qiskit_optimization", level="WARNING") as log: lineq2penalty = LinearEqualityToPenalty() _ = lineq2penalty.convert(op) warning = ( - 'WARNING:qiskit_optimization.converters.linear_equality_to_penalty:' - 'Warning: Using 100000.000000 for the penalty coefficient because a float ' - 'coefficient exists in constraints. \nThe value could be too small. If so, ' - 'set the penalty coefficient manually.' + "WARNING:qiskit_optimization.converters.linear_equality_to_penalty:" + "Warning: Using 100000.000000 for the penalty coefficient because a float " + "coefficient exists in constraints. \nThe value could be too small. If so, " + "set the penalty coefficient manually." ) self.assertIn(warning, log.output) def test_penalty_recalculation_when_reusing(self): - """ Test the penalty retrieval and recalculation of LinearEqualityToPenalty""" + """Test the penalty retrieval and recalculation of LinearEqualityToPenalty""" op = QuadraticProgram() - op.binary_var('x') - op.binary_var('y') - op.binary_var('z') - op.minimize(constant=3, linear={'x': 1}, quadratic={('x', 'y'): 2}) - op.linear_constraint(linear={'x': 1, 'y': 1, 'z': 1}, sense='EQ', rhs=2, name='xyz_eq') + op.binary_var("x") + op.binary_var("y") + op.binary_var("z") + op.minimize(constant=3, linear={"x": 1}, quadratic={("x", "y"): 2}) + op.linear_constraint( + linear={"x": 1, "y": 1, "z": 1}, sense="EQ", rhs=2, name="xyz_eq" + ) # First, create a converter with no penalty lineq2penalty = LinearEqualityToPenalty() self.assertIsNone(lineq2penalty.penalty) @@ -527,13 +565,15 @@ def test_penalty_recalculation_when_reusing(self): self.assertEqual(4, lineq2penalty.penalty) def test_linear_equality_to_penalty_decode(self): - """ Test decode func of LinearEqualityToPenalty""" + """Test decode func of LinearEqualityToPenalty""" qprog = QuadraticProgram() - qprog.binary_var('x') - qprog.binary_var('y') - qprog.binary_var('z') - qprog.maximize(linear={'x': 3, 'y': 1, 'z': 1}) - qprog.linear_constraint(linear={'x': 1, 'y': 1, 'z': 1}, sense='EQ', rhs=2, name='xyz_eq') + qprog.binary_var("x") + qprog.binary_var("y") + qprog.binary_var("z") + qprog.maximize(linear={"x": 3, "y": 1, "z": 1}) + qprog.linear_constraint( + linear={"x": 1, "y": 1, "z": 1}, sense="EQ", rhs=2, name="xyz_eq" + ) lineq2penalty = LinearEqualityToPenalty() qubo = lineq2penalty.convert(qprog) exact_mes = NumPyMinimumEigensolver() @@ -546,50 +586,64 @@ def test_linear_equality_to_penalty_decode(self): np.testing.assert_array_almost_equal(infeasible_x, [1, 1, 1]) def test_0var_range_inequality(self): - """ Test InequalityToEquality converter when the var_rang of the slack variable is 0""" + """Test InequalityToEquality converter when the var_rang of the slack variable is 0""" op = QuadraticProgram() - op.binary_var('x') - op.binary_var('y') - op.linear_constraint(linear={'x': 1, 'y': 1}, sense='LE', rhs=0, name='xy_leq1') - op.linear_constraint(linear={'x': 1, 'y': 1}, sense='GE', rhs=2, name='xy_geq1') - op.quadratic_constraint(quadratic={('x', 'x'): 1}, sense='LE', rhs=0, name='xy_leq2') - op.quadratic_constraint(quadratic={('x', 'y'): 1}, sense='GE', rhs=1, name='xy_geq2') + op.binary_var("x") + op.binary_var("y") + op.linear_constraint(linear={"x": 1, "y": 1}, sense="LE", rhs=0, name="xy_leq1") + op.linear_constraint(linear={"x": 1, "y": 1}, sense="GE", rhs=2, name="xy_geq1") + op.quadratic_constraint( + quadratic={("x", "x"): 1}, sense="LE", rhs=0, name="xy_leq2" + ) + op.quadratic_constraint( + quadratic={("x", "y"): 1}, sense="GE", rhs=1, name="xy_geq2" + ) ineq2eq = InequalityToEquality() new_op = ineq2eq.convert(op) self.assertEqual(new_op.get_num_vars(), 2) - self.assertTrue(all(l_const.sense == Constraint.Sense.EQ - for l_const in new_op.linear_constraints)) - self.assertTrue(all(q_const.sense == Constraint.Sense.EQ - for q_const in new_op.quadratic_constraints)) + self.assertTrue( + all( + l_const.sense == Constraint.Sense.EQ + for l_const in new_op.linear_constraints + ) + ) + self.assertTrue( + all( + q_const.sense == Constraint.Sense.EQ + for q_const in new_op.quadratic_constraints + ) + ) def test_integer_to_binary2(self): """Test integer to binary variables 2""" mod = QuadraticProgram() - mod.integer_var(name='x', lowerbound=0, upperbound=1) - mod.integer_var(name='y', lowerbound=0, upperbound=1) - mod.minimize(1, {'x': 1}, {('x', 'y'): 2}) - mod.linear_constraint({'x': 1}, '==', 1) - mod.quadratic_constraint({'x': 1}, {('x', 'y'): 2}, '==', 1) + mod.integer_var(name="x", lowerbound=0, upperbound=1) + mod.integer_var(name="y", lowerbound=0, upperbound=1) + mod.minimize(1, {"x": 1}, {("x", "y"): 2}) + mod.linear_constraint({"x": 1}, "==", 1) + mod.quadratic_constraint({"x": 1}, {("x", "y"): 2}, "==", 1) mod2 = IntegerToBinary().convert(mod) - self.assertListEqual([e.name + '@0' for e in mod.variables], - [e.name for e in mod2.variables]) - self.assertDictEqual(mod.objective.linear.to_dict(), - mod2.objective.linear.to_dict()) - self.assertDictEqual(mod.objective.quadratic.to_dict(), - mod2.objective.quadratic.to_dict()) - self.assertEqual(mod.get_num_linear_constraints(), - mod2.get_num_linear_constraints()) + self.assertListEqual( + [e.name + "@0" for e in mod.variables], [e.name for e in mod2.variables] + ) + self.assertDictEqual( + mod.objective.linear.to_dict(), mod2.objective.linear.to_dict() + ) + self.assertDictEqual( + mod.objective.quadratic.to_dict(), mod2.objective.quadratic.to_dict() + ) + self.assertEqual( + mod.get_num_linear_constraints(), mod2.get_num_linear_constraints() + ) for cst, cst2 in zip(mod.linear_constraints, mod2.linear_constraints): - self.assertDictEqual(cst.linear.to_dict(), - cst2.linear.to_dict()) - self.assertEqual(mod.get_num_quadratic_constraints(), - mod2.get_num_quadratic_constraints()) + self.assertDictEqual(cst.linear.to_dict(), cst2.linear.to_dict()) + self.assertEqual( + mod.get_num_quadratic_constraints(), mod2.get_num_quadratic_constraints() + ) for cst, cst2 in zip(mod.quadratic_constraints, mod2.quadratic_constraints): - self.assertDictEqual(cst.linear.to_dict(), - cst2.linear.to_dict()) - self.assertDictEqual(cst.quadratic.to_dict(), - cst2.quadratic.to_dict()) + self.assertDictEqual(cst.linear.to_dict(), cst2.linear.to_dict()) + self.assertDictEqual(cst.quadratic.to_dict(), cst2.quadratic.to_dict()) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/optimization_test_case.py b/test/optimization_test_case.py index 4b6166004..f54e37e29 100755 --- a/test/optimization_test_case.py +++ b/test/optimization_test_case.py @@ -33,6 +33,7 @@ def _noop(*args, **kargs): # disable warning messages # warnings.warn = _noop + def requires_extra_library(test_item): """Decorator that skips test if an extra library is not available Args: @@ -58,14 +59,14 @@ class QiskitOptimizationTestCase(unittest.TestCase, ABC): log = None def setUp(self) -> None: - warnings.filterwarnings('default', category=DeprecationWarning) + warnings.filterwarnings("default", category=DeprecationWarning) self._started_at = time.time() self._class_location = __file__ def tearDown(self) -> None: elapsed = time.time() - self._started_at if elapsed > 5.0: - print('({:.2f}s)'.format(round(elapsed, 2)), flush=True) + print("({:.2f}s)".format(round(elapsed, 2)), flush=True) @classmethod def setUpClass(cls) -> None: @@ -74,28 +75,27 @@ def setUpClass(cls) -> None: # Set logging to file and stdout if the LOG_LEVEL environment variable # is set. - if os.getenv('LOG_LEVEL'): + if os.getenv("LOG_LEVEL"): # Set up formatter. - log_fmt = ('{}.%(funcName)s:%(levelname)s:%(asctime)s:' - ' %(message)s'.format(cls.__name__)) + log_fmt = ( + "{}.%(funcName)s:%(levelname)s:%(asctime)s:" + " %(message)s".format(cls.__name__) + ) formatter = logging.Formatter(log_fmt) # Set up the file handler. - log_file_name = '%s.log' % cls.moduleName + log_file_name = "%s.log" % cls.moduleName file_handler = logging.FileHandler(log_file_name) file_handler.setFormatter(formatter) cls.log.addHandler(file_handler) # Set the logging level from the environment variable, defaulting # to INFO if it is not a valid level. - level = logging._nameToLevel.get(os.getenv('LOG_LEVEL'), - logging.INFO) + level = logging._nameToLevel.get(os.getenv("LOG_LEVEL"), logging.INFO) cls.log.setLevel(level) - def get_resource_path(self, - filename: str, - path: Optional[str] = None) -> str: - """ Get the absolute path to a resource. + def get_resource_path(self, filename: str, path: Optional[str] = None) -> str: + """Get the absolute path to a resource. Args: filename: filename or relative path to the resource. path: path used as relative to the filename. diff --git a/test/problems/test_linear_constraint.py b/test/problems/test_linear_constraint.py index 34b3aafed..70cb8266b 100644 --- a/test/problems/test_linear_constraint.py +++ b/test/problems/test_linear_constraint.py @@ -25,7 +25,7 @@ class TestLinearConstraint(QiskitOptimizationTestCase): """Test LinearConstraint.""" def test_init(self): - """ test init. """ + """test init.""" quadratic_program = QuadraticProgram() for _ in range(5): @@ -35,87 +35,140 @@ def test_init(self): coefficients = np.array(range(5)) # equality constraints - quadratic_program.linear_constraint(sense='==') + quadratic_program.linear_constraint(sense="==") self.assertEqual(quadratic_program.get_num_linear_constraints(), 1) - self.assertEqual(quadratic_program.linear_constraints[0].name, 'c0') - self.assertEqual(len(quadratic_program.linear_constraints[0].linear.to_dict()), 0) - self.assertEqual(quadratic_program.linear_constraints[0].sense, Constraint.Sense.EQ) + self.assertEqual(quadratic_program.linear_constraints[0].name, "c0") + self.assertEqual( + len(quadratic_program.linear_constraints[0].linear.to_dict()), 0 + ) + self.assertEqual( + quadratic_program.linear_constraints[0].sense, Constraint.Sense.EQ + ) self.assertEqual(quadratic_program.linear_constraints[0].rhs, 0.0) - self.assertEqual(quadratic_program.linear_constraints[0], - quadratic_program.get_linear_constraint('c0')) - self.assertEqual(quadratic_program.linear_constraints[0], - quadratic_program.get_linear_constraint(0)) + self.assertEqual( + quadratic_program.linear_constraints[0], + quadratic_program.get_linear_constraint("c0"), + ) + self.assertEqual( + quadratic_program.linear_constraints[0], + quadratic_program.get_linear_constraint(0), + ) with self.assertRaises(QiskitOptimizationError): - quadratic_program.linear_constraint(name='c0') + quadratic_program.linear_constraint(name="c0") - quadratic_program.linear_constraint(coefficients, '==', 1.0, 'c1') + quadratic_program.linear_constraint(coefficients, "==", 1.0, "c1") self.assertEqual(quadratic_program.get_num_linear_constraints(), 2) - self.assertEqual(quadratic_program.linear_constraints[1].name, 'c1') + self.assertEqual(quadratic_program.linear_constraints[1].name, "c1") self.assertTrue( - (quadratic_program.linear_constraints[1].linear.to_array() == coefficients).all()) - self.assertEqual(quadratic_program.linear_constraints[1].sense, Constraint.Sense.EQ) + ( + quadratic_program.linear_constraints[1].linear.to_array() + == coefficients + ).all() + ) + self.assertEqual( + quadratic_program.linear_constraints[1].sense, Constraint.Sense.EQ + ) self.assertEqual(quadratic_program.linear_constraints[1].rhs, 1.0) - self.assertEqual(quadratic_program.linear_constraints[1], - quadratic_program.get_linear_constraint('c1')) - self.assertEqual(quadratic_program.linear_constraints[1], - quadratic_program.get_linear_constraint(1)) + self.assertEqual( + quadratic_program.linear_constraints[1], + quadratic_program.get_linear_constraint("c1"), + ) + self.assertEqual( + quadratic_program.linear_constraints[1], + quadratic_program.get_linear_constraint(1), + ) # geq constraints - quadratic_program.linear_constraint(sense='>=') + quadratic_program.linear_constraint(sense=">=") self.assertEqual(quadratic_program.get_num_linear_constraints(), 3) - self.assertEqual(quadratic_program.linear_constraints[2].name, 'c2') - self.assertEqual(len(quadratic_program.linear_constraints[2].linear.to_dict()), - 0) - self.assertEqual(quadratic_program.linear_constraints[2].sense, Constraint.Sense.GE) + self.assertEqual(quadratic_program.linear_constraints[2].name, "c2") + self.assertEqual( + len(quadratic_program.linear_constraints[2].linear.to_dict()), 0 + ) + self.assertEqual( + quadratic_program.linear_constraints[2].sense, Constraint.Sense.GE + ) self.assertEqual(quadratic_program.linear_constraints[2].rhs, 0.0) - self.assertEqual(quadratic_program.linear_constraints[2], - quadratic_program.get_linear_constraint('c2')) - self.assertEqual(quadratic_program.linear_constraints[2], - quadratic_program.get_linear_constraint(2)) + self.assertEqual( + quadratic_program.linear_constraints[2], + quadratic_program.get_linear_constraint("c2"), + ) + self.assertEqual( + quadratic_program.linear_constraints[2], + quadratic_program.get_linear_constraint(2), + ) with self.assertRaises(QiskitOptimizationError): - quadratic_program.linear_constraint(name='c2', sense='>=') + quadratic_program.linear_constraint(name="c2", sense=">=") - quadratic_program.linear_constraint(coefficients, '>=', 1.0, 'c3') + quadratic_program.linear_constraint(coefficients, ">=", 1.0, "c3") self.assertEqual(quadratic_program.get_num_linear_constraints(), 4) - self.assertEqual(quadratic_program.linear_constraints[3].name, 'c3') + self.assertEqual(quadratic_program.linear_constraints[3].name, "c3") self.assertTrue( - (quadratic_program.linear_constraints[3].linear.to_array() == coefficients).all()) - self.assertEqual(quadratic_program.linear_constraints[3].sense, Constraint.Sense.GE) + ( + quadratic_program.linear_constraints[3].linear.to_array() + == coefficients + ).all() + ) + self.assertEqual( + quadratic_program.linear_constraints[3].sense, Constraint.Sense.GE + ) self.assertEqual(quadratic_program.linear_constraints[3].rhs, 1.0) - self.assertEqual(quadratic_program.linear_constraints[3], - quadratic_program.get_linear_constraint('c3')) - self.assertEqual(quadratic_program.linear_constraints[3], - quadratic_program.get_linear_constraint(3)) + self.assertEqual( + quadratic_program.linear_constraints[3], + quadratic_program.get_linear_constraint("c3"), + ) + self.assertEqual( + quadratic_program.linear_constraints[3], + quadratic_program.get_linear_constraint(3), + ) # leq constraints - quadratic_program.linear_constraint(sense='<=') + quadratic_program.linear_constraint(sense="<=") self.assertEqual(quadratic_program.get_num_linear_constraints(), 5) - self.assertEqual(quadratic_program.linear_constraints[4].name, 'c4') - self.assertEqual(len(quadratic_program.linear_constraints[4].linear.to_dict()), 0) - self.assertEqual(quadratic_program.linear_constraints[4].sense, Constraint.Sense.LE) + self.assertEqual(quadratic_program.linear_constraints[4].name, "c4") + self.assertEqual( + len(quadratic_program.linear_constraints[4].linear.to_dict()), 0 + ) + self.assertEqual( + quadratic_program.linear_constraints[4].sense, Constraint.Sense.LE + ) self.assertEqual(quadratic_program.linear_constraints[4].rhs, 0.0) - self.assertEqual(quadratic_program.linear_constraints[4], - quadratic_program.get_linear_constraint('c4')) - self.assertEqual(quadratic_program.linear_constraints[4], - quadratic_program.get_linear_constraint(4)) + self.assertEqual( + quadratic_program.linear_constraints[4], + quadratic_program.get_linear_constraint("c4"), + ) + self.assertEqual( + quadratic_program.linear_constraints[4], + quadratic_program.get_linear_constraint(4), + ) with self.assertRaises(QiskitOptimizationError): - quadratic_program.linear_constraint(name='c4', sense='<=') + quadratic_program.linear_constraint(name="c4", sense="<=") - quadratic_program.linear_constraint(coefficients, '<=', 1.0, 'c5') + quadratic_program.linear_constraint(coefficients, "<=", 1.0, "c5") self.assertEqual(quadratic_program.get_num_linear_constraints(), 6) - self.assertEqual(quadratic_program.linear_constraints[5].name, 'c5') + self.assertEqual(quadratic_program.linear_constraints[5].name, "c5") self.assertTrue( - (quadratic_program.linear_constraints[5].linear.to_array() == coefficients).all()) - self.assertEqual(quadratic_program.linear_constraints[5].sense, Constraint.Sense.LE) + ( + quadratic_program.linear_constraints[5].linear.to_array() + == coefficients + ).all() + ) + self.assertEqual( + quadratic_program.linear_constraints[5].sense, Constraint.Sense.LE + ) self.assertEqual(quadratic_program.linear_constraints[5].rhs, 1.0) - self.assertEqual(quadratic_program.linear_constraints[5], - quadratic_program.get_linear_constraint('c5')) - self.assertEqual(quadratic_program.linear_constraints[5], - quadratic_program.get_linear_constraint(5)) + self.assertEqual( + quadratic_program.linear_constraints[5], + quadratic_program.get_linear_constraint("c5"), + ) + self.assertEqual( + quadratic_program.linear_constraints[5], + quadratic_program.get_linear_constraint(5), + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/problems/test_linear_expression.py b/test/problems/test_linear_expression.py index d7521d7fb..f9d0535be 100644 --- a/test/problems/test_linear_expression.py +++ b/test/problems/test_linear_expression.py @@ -26,7 +26,7 @@ class TestLinearExpression(QiskitOptimizationTestCase): """Test LinearExpression.""" def test_init(self): - """ test init. """ + """test init.""" quadratic_program = QuadraticProgram() for _ in range(5): @@ -36,13 +36,15 @@ def test_init(self): coefficients_array = np.array(coefficients_list) coefficients_dok = dok_matrix([coefficients_list]) coefficients_dict_int = {i: i for i in range(1, 5)} - coefficients_dict_str = {'x{}'.format(i): i for i in range(1, 5)} - - for coeffs in [coefficients_list, - coefficients_array, - coefficients_dok, - coefficients_dict_int, - coefficients_dict_str]: + coefficients_dict_str = {"x{}".format(i): i for i in range(1, 5)} + + for coeffs in [ + coefficients_list, + coefficients_array, + coefficients_dok, + coefficients_dict_int, + coefficients_dict_str, + ]: linear = LinearExpression(quadratic_program, coeffs) self.assertEqual((linear.coefficients != coefficients_dok).nnz, 0) self.assertTrue((linear.to_array() == coefficients_list).all()) @@ -50,7 +52,7 @@ def test_init(self): self.assertDictEqual(linear.to_dict(use_name=True), coefficients_dict_str) def test_get_item(self): - """ test get_item. """ + """test get_item.""" quadratic_program = QuadraticProgram() for _ in range(5): @@ -62,7 +64,7 @@ def test_get_item(self): self.assertEqual(linear[i], v) def test_setters(self): - """ test setters. """ + """test setters.""" quadratic_program = QuadraticProgram() for _ in range(5): @@ -75,13 +77,15 @@ def test_setters(self): coefficients_array = np.array(coefficients_list) coefficients_dok = dok_matrix([coefficients_list]) coefficients_dict_int = {i: i for i in range(1, 5)} - coefficients_dict_str = {'x{}'.format(i): i for i in range(1, 5)} - - for coeffs in [coefficients_list, - coefficients_array, - coefficients_dok, - coefficients_dict_int, - coefficients_dict_str]: + coefficients_dict_str = {"x{}".format(i): i for i in range(1, 5)} + + for coeffs in [ + coefficients_list, + coefficients_array, + coefficients_dok, + coefficients_dict_int, + coefficients_dict_str, + ]: linear.coefficients = coeffs self.assertEqual((linear.coefficients != coefficients_dok).nnz, 0) self.assertTrue((linear.to_array() == coefficients_list).all()) @@ -89,7 +93,7 @@ def test_setters(self): self.assertDictEqual(linear.to_dict(use_name=True), coefficients_dict_str) def test_evaluate(self): - """ test evaluate. """ + """test evaluate.""" quadratic_program = QuadraticProgram() x = [quadratic_program.continuous_var() for _ in range(5)] @@ -100,13 +104,13 @@ def test_evaluate(self): values_list = list(range(len(x))) values_array = np.array(values_list) values_dict_int = {i: i for i in range(len(x))} - values_dict_str = {'x{}'.format(i): i for i in range(len(x))} + values_dict_str = {"x{}".format(i): i for i in range(len(x))} for values in [values_list, values_array, values_dict_int, values_dict_str]: self.assertEqual(linear.evaluate(values), 30) def test_evaluate_gradient(self): - """ test evaluate gradient. """ + """test evaluate gradient.""" quadratic_program = QuadraticProgram() x = [quadratic_program.continuous_var() for _ in range(5)] @@ -117,11 +121,13 @@ def test_evaluate_gradient(self): values_list = list(range(len(x))) values_array = np.array(values_list) values_dict_int = {i: i for i in range(len(x))} - values_dict_str = {'x{}'.format(i): i for i in range(len(x))} + values_dict_str = {"x{}".format(i): i for i in range(len(x))} for values in [values_list, values_array, values_dict_int, values_dict_str]: - np.testing.assert_almost_equal(linear.evaluate_gradient(values), coefficients_list) + np.testing.assert_almost_equal( + linear.evaluate_gradient(values), coefficients_list + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/problems/test_quadratic_constraint.py b/test/problems/test_quadratic_constraint.py index b9459f0d4..5c4d9b7bf 100644 --- a/test/problems/test_quadratic_constraint.py +++ b/test/problems/test_quadratic_constraint.py @@ -25,7 +25,7 @@ class TestQuadraticConstraint(QiskitOptimizationTestCase): """Test QuadraticConstraint.""" def test_init(self): - """ test init. """ + """test init.""" quadratic_program = QuadraticProgram() for _ in range(5): @@ -40,107 +40,194 @@ def test_init(self): quadratic_coeffs = np.array(lst) # equality constraints - quadratic_program.quadratic_constraint(sense='==') + quadratic_program.quadratic_constraint(sense="==") self.assertEqual(quadratic_program.get_num_quadratic_constraints(), 1) - self.assertEqual(quadratic_program.quadratic_constraints[0].name, 'q0') - self.assertEqual(len(quadratic_program.quadratic_constraints[0].linear.to_dict()), 0) - self.assertEqual(len(quadratic_program.quadratic_constraints[0].quadratic.to_dict()), 0) - self.assertEqual(quadratic_program.quadratic_constraints[0].sense, Constraint.Sense.EQ) + self.assertEqual(quadratic_program.quadratic_constraints[0].name, "q0") + self.assertEqual( + len(quadratic_program.quadratic_constraints[0].linear.to_dict()), 0 + ) + self.assertEqual( + len(quadratic_program.quadratic_constraints[0].quadratic.to_dict()), 0 + ) + self.assertEqual( + quadratic_program.quadratic_constraints[0].sense, Constraint.Sense.EQ + ) self.assertEqual(quadratic_program.quadratic_constraints[0].rhs, 0.0) - self.assertEqual(quadratic_program.quadratic_constraints[0], - quadratic_program.get_quadratic_constraint('q0')) - self.assertEqual(quadratic_program.quadratic_constraints[0], - quadratic_program.get_quadratic_constraint(0)) - - self.assertEqual(quadratic_program.quadratic_constraints[0].evaluate(linear_coeffs), 0.0) + self.assertEqual( + quadratic_program.quadratic_constraints[0], + quadratic_program.get_quadratic_constraint("q0"), + ) + self.assertEqual( + quadratic_program.quadratic_constraints[0], + quadratic_program.get_quadratic_constraint(0), + ) + + self.assertEqual( + quadratic_program.quadratic_constraints[0].evaluate(linear_coeffs), 0.0 + ) with self.assertRaises(QiskitOptimizationError): - quadratic_program.quadratic_constraint(name='q0', sense='==') + quadratic_program.quadratic_constraint(name="q0", sense="==") - quadratic_program.quadratic_constraint(linear_coeffs, quadratic_coeffs, '==', 1.0, 'q1') + quadratic_program.quadratic_constraint( + linear_coeffs, quadratic_coeffs, "==", 1.0, "q1" + ) self.assertEqual(quadratic_program.get_num_quadratic_constraints(), 2) - self.assertEqual(quadratic_program.quadratic_constraints[1].name, 'q1') + self.assertEqual(quadratic_program.quadratic_constraints[1].name, "q1") self.assertTrue( - (quadratic_program.quadratic_constraints[1].linear.to_array() == linear_coeffs).all()) + ( + quadratic_program.quadratic_constraints[1].linear.to_array() + == linear_coeffs + ).all() + ) self.assertTrue( - (quadratic_program.quadratic_constraints[1].quadratic.to_array() == - quadratic_coeffs).all()) - self.assertEqual(quadratic_program.quadratic_constraints[1].sense, Constraint.Sense.EQ) + ( + quadratic_program.quadratic_constraints[1].quadratic.to_array() + == quadratic_coeffs + ).all() + ) + self.assertEqual( + quadratic_program.quadratic_constraints[1].sense, Constraint.Sense.EQ + ) self.assertEqual(quadratic_program.quadratic_constraints[1].rhs, 1.0) - self.assertEqual(quadratic_program.quadratic_constraints[1], - quadratic_program.get_quadratic_constraint('q1')) - self.assertEqual(quadratic_program.quadratic_constraints[1], - quadratic_program.get_quadratic_constraint(1)) - - self.assertEqual(quadratic_program.quadratic_constraints[1].evaluate(linear_coeffs), 930.0) + self.assertEqual( + quadratic_program.quadratic_constraints[1], + quadratic_program.get_quadratic_constraint("q1"), + ) + self.assertEqual( + quadratic_program.quadratic_constraints[1], + quadratic_program.get_quadratic_constraint(1), + ) + + self.assertEqual( + quadratic_program.quadratic_constraints[1].evaluate(linear_coeffs), 930.0 + ) # geq constraints - quadratic_program.quadratic_constraint(sense='>=') + quadratic_program.quadratic_constraint(sense=">=") self.assertEqual(quadratic_program.get_num_quadratic_constraints(), 3) - self.assertEqual(quadratic_program.quadratic_constraints[2].name, 'q2') - self.assertEqual(len(quadratic_program.quadratic_constraints[2].linear.to_dict()), 0) - self.assertEqual(len(quadratic_program.quadratic_constraints[2].quadratic.to_dict()), 0) - self.assertEqual(quadratic_program.quadratic_constraints[2].sense, Constraint.Sense.GE) + self.assertEqual(quadratic_program.quadratic_constraints[2].name, "q2") + self.assertEqual( + len(quadratic_program.quadratic_constraints[2].linear.to_dict()), 0 + ) + self.assertEqual( + len(quadratic_program.quadratic_constraints[2].quadratic.to_dict()), 0 + ) + self.assertEqual( + quadratic_program.quadratic_constraints[2].sense, Constraint.Sense.GE + ) self.assertEqual(quadratic_program.quadratic_constraints[2].rhs, 0.0) - self.assertEqual(quadratic_program.quadratic_constraints[2], - quadratic_program.get_quadratic_constraint('q2')) - self.assertEqual(quadratic_program.quadratic_constraints[2], - quadratic_program.get_quadratic_constraint(2)) - - self.assertEqual(quadratic_program.quadratic_constraints[2].evaluate(linear_coeffs), 0.0) + self.assertEqual( + quadratic_program.quadratic_constraints[2], + quadratic_program.get_quadratic_constraint("q2"), + ) + self.assertEqual( + quadratic_program.quadratic_constraints[2], + quadratic_program.get_quadratic_constraint(2), + ) + + self.assertEqual( + quadratic_program.quadratic_constraints[2].evaluate(linear_coeffs), 0.0 + ) with self.assertRaises(QiskitOptimizationError): - quadratic_program.quadratic_constraint(name='q2', sense='>=') + quadratic_program.quadratic_constraint(name="q2", sense=">=") - quadratic_program.quadratic_constraint(linear_coeffs, quadratic_coeffs, '>=', 1.0, 'q3') + quadratic_program.quadratic_constraint( + linear_coeffs, quadratic_coeffs, ">=", 1.0, "q3" + ) self.assertEqual(quadratic_program.get_num_quadratic_constraints(), 4) - self.assertEqual(quadratic_program.quadratic_constraints[3].name, 'q3') + self.assertEqual(quadratic_program.quadratic_constraints[3].name, "q3") + self.assertTrue( + ( + quadratic_program.quadratic_constraints[3].linear.to_array() + == linear_coeffs + ).all() + ) self.assertTrue( - (quadratic_program.quadratic_constraints[3].linear.to_array() == linear_coeffs).all()) - self.assertTrue((quadratic_program.quadratic_constraints[3].quadratic.to_array() == - quadratic_coeffs).all()) - self.assertEqual(quadratic_program.quadratic_constraints[3].sense, Constraint.Sense.GE) + ( + quadratic_program.quadratic_constraints[3].quadratic.to_array() + == quadratic_coeffs + ).all() + ) + self.assertEqual( + quadratic_program.quadratic_constraints[3].sense, Constraint.Sense.GE + ) self.assertEqual(quadratic_program.quadratic_constraints[3].rhs, 1.0) - self.assertEqual(quadratic_program.quadratic_constraints[3], - quadratic_program.get_quadratic_constraint('q3')) - self.assertEqual(quadratic_program.quadratic_constraints[3], - quadratic_program.get_quadratic_constraint(3)) - - self.assertEqual(quadratic_program.quadratic_constraints[3].evaluate(linear_coeffs), 930.0) + self.assertEqual( + quadratic_program.quadratic_constraints[3], + quadratic_program.get_quadratic_constraint("q3"), + ) + self.assertEqual( + quadratic_program.quadratic_constraints[3], + quadratic_program.get_quadratic_constraint(3), + ) + + self.assertEqual( + quadratic_program.quadratic_constraints[3].evaluate(linear_coeffs), 930.0 + ) # leq constraints - quadratic_program.quadratic_constraint(sense='<=') + quadratic_program.quadratic_constraint(sense="<=") self.assertEqual(quadratic_program.get_num_quadratic_constraints(), 5) - self.assertEqual(quadratic_program.quadratic_constraints[4].name, 'q4') - self.assertEqual(len(quadratic_program.quadratic_constraints[4].linear.to_dict()), 0) - self.assertEqual(quadratic_program.quadratic_constraints[4].sense, Constraint.Sense.LE) + self.assertEqual(quadratic_program.quadratic_constraints[4].name, "q4") + self.assertEqual( + len(quadratic_program.quadratic_constraints[4].linear.to_dict()), 0 + ) + self.assertEqual( + quadratic_program.quadratic_constraints[4].sense, Constraint.Sense.LE + ) self.assertEqual(quadratic_program.quadratic_constraints[4].rhs, 0.0) - self.assertEqual(quadratic_program.quadratic_constraints[4], - quadratic_program.get_quadratic_constraint('q4')) - self.assertEqual(quadratic_program.quadratic_constraints[4], - quadratic_program.get_quadratic_constraint(4)) - - self.assertEqual(quadratic_program.quadratic_constraints[4].evaluate(linear_coeffs), 0.0) + self.assertEqual( + quadratic_program.quadratic_constraints[4], + quadratic_program.get_quadratic_constraint("q4"), + ) + self.assertEqual( + quadratic_program.quadratic_constraints[4], + quadratic_program.get_quadratic_constraint(4), + ) + + self.assertEqual( + quadratic_program.quadratic_constraints[4].evaluate(linear_coeffs), 0.0 + ) with self.assertRaises(QiskitOptimizationError): - quadratic_program.quadratic_constraint(name='q4', sense='<=') + quadratic_program.quadratic_constraint(name="q4", sense="<=") - quadratic_program.quadratic_constraint(linear_coeffs, quadratic_coeffs, '<=', 1.0, 'q5') + quadratic_program.quadratic_constraint( + linear_coeffs, quadratic_coeffs, "<=", 1.0, "q5" + ) self.assertEqual(quadratic_program.get_num_quadratic_constraints(), 6) - self.assertEqual(quadratic_program.quadratic_constraints[5].name, 'q5') + self.assertEqual(quadratic_program.quadratic_constraints[5].name, "q5") + self.assertTrue( + ( + quadratic_program.quadratic_constraints[5].linear.to_array() + == linear_coeffs + ).all() + ) self.assertTrue( - (quadratic_program.quadratic_constraints[5].linear.to_array() == linear_coeffs).all()) - self.assertTrue((quadratic_program.quadratic_constraints[5].quadratic.to_array() == - quadratic_coeffs).all()) - self.assertEqual(quadratic_program.quadratic_constraints[5].sense, Constraint.Sense.LE) + ( + quadratic_program.quadratic_constraints[5].quadratic.to_array() + == quadratic_coeffs + ).all() + ) + self.assertEqual( + quadratic_program.quadratic_constraints[5].sense, Constraint.Sense.LE + ) self.assertEqual(quadratic_program.quadratic_constraints[5].rhs, 1.0) - self.assertEqual(quadratic_program.quadratic_constraints[5], - quadratic_program.get_quadratic_constraint('q5')) - self.assertEqual(quadratic_program.quadratic_constraints[5], - quadratic_program.get_quadratic_constraint(5)) + self.assertEqual( + quadratic_program.quadratic_constraints[5], + quadratic_program.get_quadratic_constraint("q5"), + ) + self.assertEqual( + quadratic_program.quadratic_constraints[5], + quadratic_program.get_quadratic_constraint(5), + ) - self.assertEqual(quadratic_program.quadratic_constraints[5].evaluate(linear_coeffs), 930.0) + self.assertEqual( + quadratic_program.quadratic_constraints[5].evaluate(linear_coeffs), 930.0 + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/problems/test_quadratic_expression.py b/test/problems/test_quadratic_expression.py index cf27a3ff3..e11f22f88 100644 --- a/test/problems/test_quadratic_expression.py +++ b/test/problems/test_quadratic_expression.py @@ -26,7 +26,7 @@ class TestQuadraticExpression(QiskitOptimizationTestCase): """Test QuadraticExpression.""" def test_init(self): - """ test init. """ + """test init.""" quadratic_program = QuadraticProgram() for _ in range(5): @@ -39,22 +39,30 @@ def test_init(self): coefficients_array = np.array(coefficients_list) coefficients_dok = dok_matrix(coefficients_list) coefficients_dict_int = {(i, j): v for (i, j), v in coefficients_dok.items()} - coefficients_dict_str = {('x{}'.format(i), 'x{}'.format(j)): v for (i, j), v in - coefficients_dok.items()} - - for coeffs in [coefficients_list, - coefficients_array, - coefficients_dok, - coefficients_dict_int, - coefficients_dict_str]: + coefficients_dict_str = { + ("x{}".format(i), "x{}".format(j)): v + for (i, j), v in coefficients_dok.items() + } + + for coeffs in [ + coefficients_list, + coefficients_array, + coefficients_dok, + coefficients_dict_int, + coefficients_dict_str, + ]: quadratic = QuadraticExpression(quadratic_program, coeffs) self.assertEqual((quadratic.coefficients != coefficients_dok).nnz, 0) self.assertTrue((quadratic.to_array() == coefficients_list).all()) - self.assertDictEqual(quadratic.to_dict(use_name=False), coefficients_dict_int) - self.assertDictEqual(quadratic.to_dict(use_name=True), coefficients_dict_str) + self.assertDictEqual( + quadratic.to_dict(use_name=False), coefficients_dict_int + ) + self.assertDictEqual( + quadratic.to_dict(use_name=True), coefficients_dict_str + ) def test_get_item(self): - """ test get_item. """ + """test get_item.""" quadratic_program = QuadraticProgram() for _ in range(5): @@ -70,10 +78,12 @@ def test_get_item(self): if i == j: self.assertEqual(quadratic[i, j], coefficients[i][j]) else: - self.assertEqual(quadratic[i, j], coefficients[i][j] + coefficients[j][i]) + self.assertEqual( + quadratic[i, j], coefficients[i][j] + coefficients[j][i] + ) def test_setters(self): - """ test setters. """ + """test setters.""" quadratic_program = QuadraticProgram() for _ in range(5): @@ -90,22 +100,30 @@ def test_setters(self): coefficients_array = np.array(coefficients_list) coefficients_dok = dok_matrix(coefficients_list) coefficients_dict_int = {(i, j): v for (i, j), v in coefficients_dok.items()} - coefficients_dict_str = {('x{}'.format(i), 'x{}'.format(j)): v for (i, j), v in - coefficients_dok.items()} - - for coeffs in [coefficients_list, - coefficients_array, - coefficients_dok, - coefficients_dict_int, - coefficients_dict_str]: + coefficients_dict_str = { + ("x{}".format(i), "x{}".format(j)): v + for (i, j), v in coefficients_dok.items() + } + + for coeffs in [ + coefficients_list, + coefficients_array, + coefficients_dok, + coefficients_dict_int, + coefficients_dict_str, + ]: quadratic.coefficients = coeffs self.assertEqual((quadratic.coefficients != coefficients_dok).nnz, 0) self.assertTrue((quadratic.to_array() == coefficients_list).all()) - self.assertDictEqual(quadratic.to_dict(use_name=False), coefficients_dict_int) - self.assertDictEqual(quadratic.to_dict(use_name=True), coefficients_dict_str) + self.assertDictEqual( + quadratic.to_dict(use_name=False), coefficients_dict_int + ) + self.assertDictEqual( + quadratic.to_dict(use_name=True), coefficients_dict_str + ) def test_evaluate(self): - """ test evaluate. """ + """test evaluate.""" quadratic_program = QuadraticProgram() x = [quadratic_program.continuous_var() for _ in range(5)] @@ -119,13 +137,13 @@ def test_evaluate(self): values_list = list(range(len(x))) values_array = np.array(values_list) values_dict_int = {i: i for i in range(len(x))} - values_dict_str = {'x{}'.format(i): i for i in range(len(x))} + values_dict_str = {"x{}".format(i): i for i in range(len(x))} for values in [values_list, values_array, values_dict_int, values_dict_str]: self.assertEqual(quadratic.evaluate(values), 900) def test_evaluate_gradient(self): - """ test evaluate gradient. """ + """test evaluate gradient.""" quadratic_program = QuadraticProgram() x = [quadratic_program.continuous_var() for _ in range(5)] @@ -139,23 +157,29 @@ def test_evaluate_gradient(self): values_list = list(range(len(x))) values_array = np.array(values_list) values_dict_int = {i: i for i in range(len(x))} - values_dict_str = {'x{}'.format(i): i for i in range(len(x))} + values_dict_str = {"x{}".format(i): i for i in range(len(x))} - grad_values = [0., 60., 120., 180., 240.] + grad_values = [0.0, 60.0, 120.0, 180.0, 240.0] for values in [values_list, values_array, values_dict_int, values_dict_str]: - np.testing.assert_almost_equal(quadratic.evaluate_gradient(values), grad_values) + np.testing.assert_almost_equal( + quadratic.evaluate_gradient(values), grad_values + ) def test_symmetric_set(self): - """ test symmetric set """ + """test symmetric set""" q_p = QuadraticProgram() - q_p.binary_var('x') - q_p.binary_var('y') - q_p.binary_var('z') - quad = QuadraticExpression(q_p, {('x', 'y'): -1, ('y', 'x'): 2, ('z', 'x'): 3}) - self.assertDictEqual(quad.to_dict(use_name=True), {('x', 'y'): 1, ('x', 'z'): 3}) - self.assertDictEqual(quad.to_dict(symmetric=True, use_name=True), - {('x', 'y'): 0.5, ('y', 'x'): 0.5, ('x', 'z'): 1.5, ('z', 'x'): 1.5}) - - -if __name__ == '__main__': + q_p.binary_var("x") + q_p.binary_var("y") + q_p.binary_var("z") + quad = QuadraticExpression(q_p, {("x", "y"): -1, ("y", "x"): 2, ("z", "x"): 3}) + self.assertDictEqual( + quad.to_dict(use_name=True), {("x", "y"): 1, ("x", "z"): 3} + ) + self.assertDictEqual( + quad.to_dict(symmetric=True, use_name=True), + {("x", "y"): 0.5, ("y", "x"): 0.5, ("x", "z"): 1.5, ("z", "x"): 1.5}, + ) + + +if __name__ == "__main__": unittest.main() diff --git a/test/problems/test_quadratic_objective.py b/test/problems/test_quadratic_objective.py index e3da08dce..377797b1a 100644 --- a/test/problems/test_quadratic_objective.py +++ b/test/problems/test_quadratic_objective.py @@ -24,7 +24,7 @@ class TestQuadraticObjective(QiskitOptimizationTestCase): """Test QuadraticObjective""" def test_init(self): - """ test init. """ + """test init.""" quadratic_program = QuadraticProgram() for _ in range(5): @@ -33,7 +33,9 @@ def test_init(self): self.assertEqual(quadratic_program.objective.constant, 0.0) self.assertEqual(len(quadratic_program.objective.linear.to_dict()), 0) self.assertEqual(len(quadratic_program.objective.quadratic.to_dict()), 0) - self.assertEqual(quadratic_program.objective.sense, QuadraticObjective.Sense.MINIMIZE) + self.assertEqual( + quadratic_program.objective.sense, QuadraticObjective.Sense.MINIMIZE + ) constant = 1.0 linear_coeffs = np.array(range(5)) @@ -46,27 +48,38 @@ def test_init(self): quadratic_program.minimize(constant, linear_coeffs, quadratic_coeffs) self.assertEqual(quadratic_program.objective.constant, constant) - self.assertTrue((quadratic_program.objective.linear.to_array() == linear_coeffs).all()) self.assertTrue( - (quadratic_program.objective.quadratic.to_array() == quadratic_coeffs).all()) - self.assertEqual(quadratic_program.objective.sense, QuadraticObjective.Sense.MINIMIZE) + (quadratic_program.objective.linear.to_array() == linear_coeffs).all() + ) + self.assertTrue( + (quadratic_program.objective.quadratic.to_array() == quadratic_coeffs).all() + ) + self.assertEqual( + quadratic_program.objective.sense, QuadraticObjective.Sense.MINIMIZE + ) quadratic_program.maximize(constant, linear_coeffs, quadratic_coeffs) self.assertEqual(quadratic_program.objective.constant, constant) - self.assertTrue((quadratic_program.objective.linear.to_array() == linear_coeffs).all()) self.assertTrue( - (quadratic_program.objective.quadratic.to_array() == quadratic_coeffs).all()) - self.assertEqual(quadratic_program.objective.sense, QuadraticObjective.Sense.MAXIMIZE) + (quadratic_program.objective.linear.to_array() == linear_coeffs).all() + ) + self.assertTrue( + (quadratic_program.objective.quadratic.to_array() == quadratic_coeffs).all() + ) + self.assertEqual( + quadratic_program.objective.sense, QuadraticObjective.Sense.MAXIMIZE + ) self.assertEqual(quadratic_program.objective.evaluate(linear_coeffs), 931.0) - grad_values = [0., 61., 122., 183., 244.] - np.testing.assert_almost_equal(quadratic_program.objective.evaluate_gradient(linear_coeffs), - grad_values) + grad_values = [0.0, 61.0, 122.0, 183.0, 244.0] + np.testing.assert_almost_equal( + quadratic_program.objective.evaluate_gradient(linear_coeffs), grad_values + ) def test_setters(self): - """ test setters. """ + """test setters.""" quadratic_program = QuadraticProgram() for _ in range(5): @@ -85,18 +98,27 @@ def test_setters(self): quadratic_program.objective.quadratic = quadratic_coeffs self.assertEqual(quadratic_program.objective.constant, constant) - self.assertTrue((quadratic_program.objective.linear.to_array() == linear_coeffs).all()) self.assertTrue( - (quadratic_program.objective.quadratic.to_array() == quadratic_coeffs).all()) + (quadratic_program.objective.linear.to_array() == linear_coeffs).all() + ) + self.assertTrue( + (quadratic_program.objective.quadratic.to_array() == quadratic_coeffs).all() + ) - self.assertEqual(quadratic_program.objective.sense, QuadraticObjective.Sense.MINIMIZE) + self.assertEqual( + quadratic_program.objective.sense, QuadraticObjective.Sense.MINIMIZE + ) quadratic_program.objective.sense = quadratic_program.objective.Sense.MAXIMIZE - self.assertEqual(quadratic_program.objective.sense, QuadraticObjective.Sense.MAXIMIZE) + self.assertEqual( + quadratic_program.objective.sense, QuadraticObjective.Sense.MAXIMIZE + ) quadratic_program.objective.sense = quadratic_program.objective.Sense.MINIMIZE - self.assertEqual(quadratic_program.objective.sense, QuadraticObjective.Sense.MINIMIZE) + self.assertEqual( + quadratic_program.objective.sense, QuadraticObjective.Sense.MINIMIZE + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/problems/test_quadratic_program.py b/test/problems/test_quadratic_program.py index 86bcb0a54..18d19eff0 100644 --- a/test/problems/test_quadratic_program.py +++ b/test/problems/test_quadratic_program.py @@ -15,12 +15,20 @@ import tempfile import unittest from os import path -from test.optimization_test_case import QiskitOptimizationTestCase, requires_extra_library +from test.optimization_test_case import ( + QiskitOptimizationTestCase, + requires_extra_library, +) from docplex.mp.model import DOcplexException, Model from qiskit_optimization import INFINITY, QiskitOptimizationError, QuadraticProgram -from qiskit_optimization.problems import Constraint, QuadraticObjective, Variable, VarType +from qiskit_optimization.problems import ( + Constraint, + QuadraticObjective, + Variable, + VarType, +) class TestQuadraticProgram(QiskitOptimizationTestCase): @@ -28,9 +36,9 @@ class TestQuadraticProgram(QiskitOptimizationTestCase): (VariablesInterface, etc).""" def test_constructor(self): - """ test constructor """ + """test constructor""" quadratic_program = QuadraticProgram() - self.assertEqual(quadratic_program.name, '') + self.assertEqual(quadratic_program.name, "") self.assertEqual(quadratic_program.status, QuadraticProgram.Status.VALID) self.assertEqual(quadratic_program.get_num_vars(), 0) self.assertEqual(quadratic_program.get_num_linear_constraints(), 0) @@ -40,15 +48,15 @@ def test_constructor(self): self.assertDictEqual(quadratic_program.objective.quadratic.to_dict(), {}) def test_clear(self): - """ test clear """ - q_p = QuadraticProgram('test') - q_p.binary_var('x') - q_p.binary_var('y') - q_p.minimize(constant=1, linear={'x': 1, 'y': 2}, quadratic={('x', 'x'): 1}) - q_p.linear_constraint({'x': 1}, '==', 1) - q_p.quadratic_constraint({'x': 1}, {('y', 'y'): 2}, '<=', 1) + """test clear""" + q_p = QuadraticProgram("test") + q_p.binary_var("x") + q_p.binary_var("y") + q_p.minimize(constant=1, linear={"x": 1, "y": 2}, quadratic={("x", "x"): 1}) + q_p.linear_constraint({"x": 1}, "==", 1) + q_p.quadratic_constraint({"x": 1}, {("y", "y"): 2}, "<=", 1) q_p.clear() - self.assertEqual(q_p.name, '') + self.assertEqual(q_p.name, "") self.assertEqual(q_p.status, QuadraticProgram.Status.VALID) self.assertEqual(q_p.get_num_vars(), 0) self.assertEqual(q_p.get_num_linear_constraints(), 0) @@ -58,10 +66,10 @@ def test_clear(self): self.assertDictEqual(q_p.objective.quadratic.to_dict(), {}) def test_name_setter(self): - """ test name setter """ + """test name setter""" q_p = QuadraticProgram() - self.assertEqual(q_p.name, '') - name = 'test name' + self.assertEqual(q_p.name, "") + name = "test name" q_p.name = name self.assertEqual(q_p.name, name) @@ -76,40 +84,60 @@ def test_var_dict(self): """test {binary,integer,continuous}_var_dict""" q_p = QuadraticProgram() - d_0 = q_p.continuous_var_dict(name='a', key_format='_{}', keys=3) - self.assertSetEqual(set(d_0.keys()), {'a_0', 'a_1', 'a_2'}) - self.assertSetEqual({var.name for var in q_p.variables}, {'a_0', 'a_1', 'a_2'}) + d_0 = q_p.continuous_var_dict(name="a", key_format="_{}", keys=3) + self.assertSetEqual(set(d_0.keys()), {"a_0", "a_1", "a_2"}) + self.assertSetEqual({var.name for var in q_p.variables}, {"a_0", "a_1", "a_2"}) for var in q_p.variables: self.assertAlmostEqual(var.lowerbound, 0) self.assertAlmostEqual(var.upperbound, INFINITY) self.assertEqual(var.vartype, VarType.CONTINUOUS) self.assertTupleEqual(var.as_tuple(), d_0[var.name].as_tuple()) - d_1 = q_p.binary_var_dict(name='b', keys=5) - self.assertSetEqual(set(d_1.keys()), {'b3', 'b4', 'b5', 'b6', 'b7'}) - self.assertSetEqual({var.name for var in q_p.variables}, - {'a_0', 'a_1', 'a_2', 'b3', 'b4', 'b5', 'b6', 'b7'}) + d_1 = q_p.binary_var_dict(name="b", keys=5) + self.assertSetEqual(set(d_1.keys()), {"b3", "b4", "b5", "b6", "b7"}) + self.assertSetEqual( + {var.name for var in q_p.variables}, + {"a_0", "a_1", "a_2", "b3", "b4", "b5", "b6", "b7"}, + ) for var in q_p.variables[-5:]: self.assertAlmostEqual(var.lowerbound, 0) self.assertAlmostEqual(var.upperbound, 1) self.assertEqual(var.vartype, VarType.BINARY) self.assertTupleEqual(var.as_tuple(), d_1[var.name].as_tuple()) - d_2 = q_p.integer_var_dict(keys=1, key_format='-{}', lowerbound=-4, upperbound=10) - self.assertSetEqual(set(d_2.keys()), {'x-8'}) - self.assertSetEqual({var.name for var in q_p.variables}, - {'a_0', 'a_1', 'a_2', 'b3', 'b4', 'b5', 'b6', 'b7', 'x-8'}) + d_2 = q_p.integer_var_dict( + keys=1, key_format="-{}", lowerbound=-4, upperbound=10 + ) + self.assertSetEqual(set(d_2.keys()), {"x-8"}) + self.assertSetEqual( + {var.name for var in q_p.variables}, + {"a_0", "a_1", "a_2", "b3", "b4", "b5", "b6", "b7", "x-8"}, + ) for var in q_p.variables[-1:]: self.assertAlmostEqual(var.lowerbound, -4) self.assertAlmostEqual(var.upperbound, 10) self.assertEqual(var.vartype, VarType.INTEGER) self.assertTupleEqual(var.as_tuple(), d_2[var.name].as_tuple()) - d_3 = q_p.binary_var_dict(name='c', keys=range(3)) - self.assertSetEqual(set(d_3.keys()), {'c0', 'c1', 'c2'}) - self.assertSetEqual({var.name for var in q_p.variables}, - {'a_0', 'a_1', 'a_2', 'b3', 'b4', 'b5', 'b6', 'b7', 'x-8', - 'c0', 'c1', 'c2'}) + d_3 = q_p.binary_var_dict(name="c", keys=range(3)) + self.assertSetEqual(set(d_3.keys()), {"c0", "c1", "c2"}) + self.assertSetEqual( + {var.name for var in q_p.variables}, + { + "a_0", + "a_1", + "a_2", + "b3", + "b4", + "b5", + "b6", + "b7", + "x-8", + "c0", + "c1", + "c2", + }, + ) for var in q_p.variables[-3:]: self.assertAlmostEqual(var.lowerbound, 0) self.assertAlmostEqual(var.upperbound, 1) @@ -117,13 +145,28 @@ def test_var_dict(self): self.assertTupleEqual(var.as_tuple(), d_3[var.name].as_tuple()) with self.assertRaises(QiskitOptimizationError): - q_p.binary_var_dict(name='c', keys=range(3)) - - d_4 = q_p.binary_var_dict(1, 'x', '_') - self.assertSetEqual(set(d_4.keys()), {'x_'}) - self.assertSetEqual({var.name for var in q_p.variables}, - {'a_0', 'a_1', 'a_2', 'b3', 'b4', 'b5', 'b6', 'b7', 'x-8', - 'c0', 'c1', 'c2', 'x_'}) + q_p.binary_var_dict(name="c", keys=range(3)) + + d_4 = q_p.binary_var_dict(1, "x", "_") + self.assertSetEqual(set(d_4.keys()), {"x_"}) + self.assertSetEqual( + {var.name for var in q_p.variables}, + { + "a_0", + "a_1", + "a_2", + "b3", + "b4", + "b5", + "b6", + "b7", + "x-8", + "c0", + "c1", + "c2", + "x_", + }, + ) for var in q_p.variables[-1:]: self.assertAlmostEqual(var.lowerbound, 0) self.assertAlmostEqual(var.upperbound, 1) @@ -131,16 +174,32 @@ def test_var_dict(self): self.assertTupleEqual(var.as_tuple(), d_4[var.name].as_tuple()) with self.assertRaises(QiskitOptimizationError): - q_p.binary_var_dict(1, 'x', '_') + q_p.binary_var_dict(1, "x", "_") with self.assertRaises(QiskitOptimizationError): - q_p.binary_var('x_') - - d_5 = q_p.continuous_var_dict(1, -1, 2, '', '') - self.assertSetEqual(set(d_5.keys()), {''}) - self.assertSetEqual({var.name for var in q_p.variables}, - {'a_0', 'a_1', 'a_2', 'b3', 'b4', 'b5', 'b6', 'b7', 'x-8', - 'c0', 'c1', 'c2', 'x_', ''}) + q_p.binary_var("x_") + + d_5 = q_p.continuous_var_dict(1, -1, 2, "", "") + self.assertSetEqual(set(d_5.keys()), {""}) + self.assertSetEqual( + {var.name for var in q_p.variables}, + { + "a_0", + "a_1", + "a_2", + "b3", + "b4", + "b5", + "b6", + "b7", + "x-8", + "c0", + "c1", + "c2", + "x_", + "", + }, + ) for var in q_p.variables[-1:]: self.assertAlmostEqual(var.lowerbound, -1) self.assertAlmostEqual(var.upperbound, 2) @@ -148,14 +207,14 @@ def test_var_dict(self): self.assertTupleEqual(var.as_tuple(), d_5[var.name].as_tuple()) with self.assertRaises(QiskitOptimizationError): - q_p.binary_var_dict(1, '', '') + q_p.binary_var_dict(1, "", "") with self.assertRaises(QiskitOptimizationError): - q_p.integer_var(0, 1, '') + q_p.integer_var(0, 1, "") with self.assertRaises(QiskitOptimizationError): q_p = QuadraticProgram() - q_p.binary_var_dict(keys=1, key_format='{}{}') + q_p.binary_var_dict(keys=1, key_format="{}{}") with self.assertRaises(QiskitOptimizationError): q_p = QuadraticProgram() @@ -163,23 +222,23 @@ def test_var_dict(self): with self.assertRaises(QiskitOptimizationError): q_p = QuadraticProgram() - q_p.binary_var_dict(keys=1, key_format='_{{}}') + q_p.binary_var_dict(keys=1, key_format="_{{}}") with self.assertRaises(QiskitOptimizationError): q_p = QuadraticProgram() - q_p.binary_var_dict(keys=2, key_format='') + q_p.binary_var_dict(keys=2, key_format="") with self.assertRaises(QiskitOptimizationError): q_p = QuadraticProgram() - q_p.binary_var_dict(keys=range(2), key_format='') + q_p.binary_var_dict(keys=range(2), key_format="") def test_var_list(self): """test {binary,integer,continuous}_var_list""" q_p = QuadraticProgram() - d_0 = q_p.continuous_var_list(name='a', key_format='_{}', keys=3) - names = ['a_0', 'a_1', 'a_2'] - self.assertSetEqual({var.name for var in q_p.variables}, {'a_0', 'a_1', 'a_2'}) + d_0 = q_p.continuous_var_list(name="a", key_format="_{}", keys=3) + names = ["a_0", "a_1", "a_2"] + self.assertSetEqual({var.name for var in q_p.variables}, {"a_0", "a_1", "a_2"}) for i, var in enumerate(q_p.variables): self.assertEqual(var.name, names[i]) self.assertAlmostEqual(var.lowerbound, 0) @@ -187,10 +246,12 @@ def test_var_list(self): self.assertEqual(var.vartype, VarType.CONTINUOUS) self.assertTupleEqual(var.as_tuple(), d_0[i].as_tuple()) - d_1 = q_p.binary_var_list(name='b', keys=5) - names = ['b3', 'b4', 'b5', 'b6', 'b7'] - self.assertSetEqual({var.name for var in q_p.variables}, - {'a_0', 'a_1', 'a_2', 'b3', 'b4', 'b5', 'b6', 'b7'}) + d_1 = q_p.binary_var_list(name="b", keys=5) + names = ["b3", "b4", "b5", "b6", "b7"] + self.assertSetEqual( + {var.name for var in q_p.variables}, + {"a_0", "a_1", "a_2", "b3", "b4", "b5", "b6", "b7"}, + ) for i, var in enumerate(q_p.variables[-5:]): self.assertEqual(var.name, names[i]) self.assertAlmostEqual(var.lowerbound, 0) @@ -198,10 +259,14 @@ def test_var_list(self): self.assertEqual(var.vartype, VarType.BINARY) self.assertTupleEqual(var.as_tuple(), d_1[i].as_tuple()) - d_2 = q_p.integer_var_list(keys=1, key_format='-{}', lowerbound=-4, upperbound=10) - names = ['x-8'] - self.assertSetEqual({var.name for var in q_p.variables}, - {'a_0', 'a_1', 'a_2', 'b3', 'b4', 'b5', 'b6', 'b7', 'x-8'}) + d_2 = q_p.integer_var_list( + keys=1, key_format="-{}", lowerbound=-4, upperbound=10 + ) + names = ["x-8"] + self.assertSetEqual( + {var.name for var in q_p.variables}, + {"a_0", "a_1", "a_2", "b3", "b4", "b5", "b6", "b7", "x-8"}, + ) for i, var in enumerate(q_p.variables[-1:]): self.assertEqual(var.name, names[i]) self.assertAlmostEqual(var.lowerbound, -4) @@ -209,11 +274,25 @@ def test_var_list(self): self.assertEqual(var.vartype, VarType.INTEGER) self.assertTupleEqual(var.as_tuple(), d_2[i].as_tuple()) - d_3 = q_p.binary_var_list(name='c', keys=range(3)) - names = ['c0', 'c1', 'c2'] - self.assertSetEqual({var.name for var in q_p.variables}, - {'a_0', 'a_1', 'a_2', 'b3', 'b4', 'b5', 'b6', 'b7', 'x-8', - 'c0', 'c1', 'c2'}) + d_3 = q_p.binary_var_list(name="c", keys=range(3)) + names = ["c0", "c1", "c2"] + self.assertSetEqual( + {var.name for var in q_p.variables}, + { + "a_0", + "a_1", + "a_2", + "b3", + "b4", + "b5", + "b6", + "b7", + "x-8", + "c0", + "c1", + "c2", + }, + ) for i, var in enumerate(q_p.variables[-3:]): self.assertEqual(var.name, names[i]) self.assertAlmostEqual(var.lowerbound, 0) @@ -222,13 +301,28 @@ def test_var_list(self): self.assertTupleEqual(var.as_tuple(), d_3[i].as_tuple()) with self.assertRaises(QiskitOptimizationError): - q_p.binary_var_list(name='c', keys=range(3)) - - d_4 = q_p.binary_var_dict(1, 'x', '_') - names = ['x_'] - self.assertSetEqual({var.name for var in q_p.variables}, - {'a_0', 'a_1', 'a_2', 'b3', 'b4', 'b5', 'b6', 'b7', 'x-8', - 'c0', 'c1', 'c2', 'x_'}) + q_p.binary_var_list(name="c", keys=range(3)) + + d_4 = q_p.binary_var_dict(1, "x", "_") + names = ["x_"] + self.assertSetEqual( + {var.name for var in q_p.variables}, + { + "a_0", + "a_1", + "a_2", + "b3", + "b4", + "b5", + "b6", + "b7", + "x-8", + "c0", + "c1", + "c2", + "x_", + }, + ) for i, var in enumerate(q_p.variables[-1:]): self.assertEqual(var.name, names[i]) self.assertAlmostEqual(var.lowerbound, 0) @@ -237,16 +331,32 @@ def test_var_list(self): self.assertTupleEqual(var.as_tuple(), d_4[var.name].as_tuple()) with self.assertRaises(QiskitOptimizationError): - q_p.binary_var_list(1, 'x', '_') + q_p.binary_var_list(1, "x", "_") with self.assertRaises(QiskitOptimizationError): - q_p.binary_var('x_') - - d_5 = q_p.integer_var_list(1, -1, 2, '', '') - names = [''] - self.assertSetEqual({var.name for var in q_p.variables}, - {'a_0', 'a_1', 'a_2', 'b3', 'b4', 'b5', 'b6', 'b7', 'x-8', - 'c0', 'c1', 'c2', 'x_', ''}) + q_p.binary_var("x_") + + d_5 = q_p.integer_var_list(1, -1, 2, "", "") + names = [""] + self.assertSetEqual( + {var.name for var in q_p.variables}, + { + "a_0", + "a_1", + "a_2", + "b3", + "b4", + "b5", + "b6", + "b7", + "x-8", + "c0", + "c1", + "c2", + "x_", + "", + }, + ) for i, var in enumerate(q_p.variables[-1:]): self.assertEqual(var.name, names[i]) self.assertAlmostEqual(var.lowerbound, -1) @@ -255,14 +365,14 @@ def test_var_list(self): self.assertTupleEqual(var.as_tuple(), d_5[i].as_tuple()) with self.assertRaises(QiskitOptimizationError): - q_p.binary_var_list(1, '', '') + q_p.binary_var_list(1, "", "") with self.assertRaises(QiskitOptimizationError): - q_p.integer_var(0, 1, '') + q_p.integer_var(0, 1, "") with self.assertRaises(QiskitOptimizationError): q_p = QuadraticProgram() - q_p.binary_var_list(keys=1, key_format='{}{}') + q_p.binary_var_list(keys=1, key_format="{}{}") with self.assertRaises(QiskitOptimizationError): q_p = QuadraticProgram() @@ -270,18 +380,18 @@ def test_var_list(self): with self.assertRaises(QiskitOptimizationError): q_p = QuadraticProgram() - q_p.binary_var_list(keys=1, key_format='_{{}}') + q_p.binary_var_list(keys=1, key_format="_{{}}") with self.assertRaises(QiskitOptimizationError): q_p = QuadraticProgram() - q_p.binary_var_list(keys=2, key_format='') + q_p.binary_var_list(keys=2, key_format="") with self.assertRaises(QiskitOptimizationError): q_p = QuadraticProgram() - q_p.binary_var_list(keys=range(2), key_format='') + q_p.binary_var_list(keys=range(2), key_format="") def test_variables_handling(self): - """ test add variables """ + """test add variables""" quadratic_program = QuadraticProgram() self.assertEqual(quadratic_program.get_num_vars(), 0) @@ -290,7 +400,7 @@ def test_variables_handling(self): self.assertEqual(quadratic_program.get_num_integer_vars(), 0) x_0 = quadratic_program.continuous_var() - self.assertEqual(x_0.name, 'x0') + self.assertEqual(x_0.name, "x0") self.assertEqual(x_0.lowerbound, 0) self.assertEqual(x_0.upperbound, INFINITY) self.assertEqual(x_0.vartype, Variable.Type.CONTINUOUS) @@ -300,8 +410,8 @@ def test_variables_handling(self): self.assertEqual(quadratic_program.get_num_binary_vars(), 0) self.assertEqual(quadratic_program.get_num_integer_vars(), 0) - x_1 = quadratic_program.continuous_var(name='x1', lowerbound=5, upperbound=10) - self.assertEqual(x_1.name, 'x1') + x_1 = quadratic_program.continuous_var(name="x1", lowerbound=5, upperbound=10) + self.assertEqual(x_1.name, "x1") self.assertEqual(x_1.lowerbound, 5) self.assertEqual(x_1.upperbound, 10) self.assertEqual(x_1.vartype, Variable.Type.CONTINUOUS) @@ -312,7 +422,7 @@ def test_variables_handling(self): self.assertEqual(quadratic_program.get_num_integer_vars(), 0) x_2 = quadratic_program.binary_var() - self.assertEqual(x_2.name, 'x2') + self.assertEqual(x_2.name, "x2") self.assertEqual(x_2.lowerbound, 0) self.assertEqual(x_2.upperbound, 1) self.assertEqual(x_2.vartype, Variable.Type.BINARY) @@ -322,8 +432,8 @@ def test_variables_handling(self): self.assertEqual(quadratic_program.get_num_binary_vars(), 1) self.assertEqual(quadratic_program.get_num_integer_vars(), 0) - x_3 = quadratic_program.binary_var(name='x3') - self.assertEqual(x_3.name, 'x3') + x_3 = quadratic_program.binary_var(name="x3") + self.assertEqual(x_3.name, "x3") self.assertEqual(x_3.lowerbound, 0) self.assertEqual(x_3.upperbound, 1) self.assertEqual(x_3.vartype, Variable.Type.BINARY) @@ -334,7 +444,7 @@ def test_variables_handling(self): self.assertEqual(quadratic_program.get_num_integer_vars(), 0) x_4 = quadratic_program.integer_var() - self.assertEqual(x_4.name, 'x4') + self.assertEqual(x_4.name, "x4") self.assertEqual(x_4.lowerbound, 0) self.assertEqual(x_4.upperbound, INFINITY) self.assertEqual(x_4.vartype, Variable.Type.INTEGER) @@ -344,8 +454,8 @@ def test_variables_handling(self): self.assertEqual(quadratic_program.get_num_binary_vars(), 2) self.assertEqual(quadratic_program.get_num_integer_vars(), 1) - x_5 = quadratic_program.integer_var(name='x5', lowerbound=5, upperbound=10) - self.assertEqual(x_5.name, 'x5') + x_5 = quadratic_program.integer_var(name="x5", lowerbound=5, upperbound=10) + self.assertEqual(x_5.name, "x5") self.assertEqual(x_5.lowerbound, 5) self.assertEqual(x_5.upperbound, 10) self.assertEqual(x_5.vartype, Variable.Type.INTEGER) @@ -356,13 +466,13 @@ def test_variables_handling(self): self.assertEqual(quadratic_program.get_num_integer_vars(), 2) with self.assertRaises(QiskitOptimizationError): - quadratic_program.continuous_var(name='x0') + quadratic_program.continuous_var(name="x0") with self.assertRaises(QiskitOptimizationError): - quadratic_program.binary_var(name='x0') + quadratic_program.binary_var(name="x0") with self.assertRaises(QiskitOptimizationError): - quadratic_program.integer_var(name='x0') + quadratic_program.integer_var(name="x0") variables = [x_0, x_1, x_2, x_3, x_4, x_5] for i, x in enumerate(variables): @@ -370,236 +480,268 @@ def test_variables_handling(self): z = quadratic_program.get_variable(x.name) self.assertEqual(x.name, y.name) self.assertEqual(x.name, z.name) - self.assertDictEqual(quadratic_program.variables_index, - {'x' + str(i): i for i in range(6)}) + self.assertDictEqual( + quadratic_program.variables_index, {"x" + str(i): i for i in range(6)} + ) def test_linear_constraints_handling(self): """test linear constraints handling""" q_p = QuadraticProgram() - q_p.binary_var('x') - q_p.binary_var('y') - q_p.binary_var('z') - q_p.linear_constraint({'x': 1}, '==', 1) - q_p.linear_constraint({'y': 1}, '<=', 1) - q_p.linear_constraint({'z': 1}, '>=', 1) + q_p.binary_var("x") + q_p.binary_var("y") + q_p.binary_var("z") + q_p.linear_constraint({"x": 1}, "==", 1) + q_p.linear_constraint({"y": 1}, "<=", 1) + q_p.linear_constraint({"z": 1}, ">=", 1) self.assertEqual(q_p.get_num_linear_constraints(), 3) lin = q_p.linear_constraints self.assertEqual(len(lin), 3) self.assertDictEqual(lin[0].linear.to_dict(), {0: 1}) - self.assertDictEqual(lin[0].linear.to_dict(use_name=True), {'x': 1}) + self.assertDictEqual(lin[0].linear.to_dict(use_name=True), {"x": 1}) self.assertListEqual(lin[0].linear.to_array().tolist(), [1, 0, 0]) self.assertEqual(lin[0].sense, Constraint.Sense.EQ) self.assertEqual(lin[0].rhs, 1) - self.assertEqual(lin[0].name, 'c0') - self.assertEqual(q_p.get_linear_constraint(0).name, 'c0') - self.assertEqual(q_p.get_linear_constraint('c0').name, 'c0') + self.assertEqual(lin[0].name, "c0") + self.assertEqual(q_p.get_linear_constraint(0).name, "c0") + self.assertEqual(q_p.get_linear_constraint("c0").name, "c0") self.assertDictEqual(lin[1].linear.to_dict(), {1: 1}) - self.assertDictEqual(lin[1].linear.to_dict(use_name=True), {'y': 1}) + self.assertDictEqual(lin[1].linear.to_dict(use_name=True), {"y": 1}) self.assertListEqual(lin[1].linear.to_array().tolist(), [0, 1, 0]) self.assertEqual(lin[1].sense, Constraint.Sense.LE) self.assertEqual(lin[1].rhs, 1) - self.assertEqual(lin[1].name, 'c1') - self.assertEqual(q_p.get_linear_constraint(1).name, 'c1') - self.assertEqual(q_p.get_linear_constraint('c1').name, 'c1') + self.assertEqual(lin[1].name, "c1") + self.assertEqual(q_p.get_linear_constraint(1).name, "c1") + self.assertEqual(q_p.get_linear_constraint("c1").name, "c1") self.assertDictEqual(lin[2].linear.to_dict(), {2: 1}) - self.assertDictEqual(lin[2].linear.to_dict(use_name=True), {'z': 1}) + self.assertDictEqual(lin[2].linear.to_dict(use_name=True), {"z": 1}) self.assertListEqual(lin[2].linear.to_array().tolist(), [0, 0, 1]) self.assertEqual(lin[2].sense, Constraint.Sense.GE) self.assertEqual(lin[2].rhs, 1) - self.assertEqual(lin[2].name, 'c2') - self.assertEqual(q_p.get_linear_constraint(2).name, 'c2') - self.assertEqual(q_p.get_linear_constraint('c2').name, 'c2') + self.assertEqual(lin[2].name, "c2") + self.assertEqual(q_p.get_linear_constraint(2).name, "c2") + self.assertEqual(q_p.get_linear_constraint("c2").name, "c2") with self.assertRaises(QiskitOptimizationError): - q_p.linear_constraint(name='c0') + q_p.linear_constraint(name="c0") with self.assertRaises(QiskitOptimizationError): - q_p.linear_constraint(name='c1') + q_p.linear_constraint(name="c1") with self.assertRaises(QiskitOptimizationError): - q_p.linear_constraint(name='c2') + q_p.linear_constraint(name="c2") with self.assertRaises(IndexError): q_p.get_linear_constraint(4) with self.assertRaises(KeyError): - q_p.get_linear_constraint('c3') + q_p.get_linear_constraint("c3") - q_p.remove_linear_constraint('c1') + q_p.remove_linear_constraint("c1") lin = q_p.linear_constraints self.assertEqual(len(lin), 2) self.assertDictEqual(lin[1].linear.to_dict(), {2: 1}) - self.assertDictEqual(lin[1].linear.to_dict(use_name=True), {'z': 1}) + self.assertDictEqual(lin[1].linear.to_dict(use_name=True), {"z": 1}) self.assertListEqual(lin[1].linear.to_array().tolist(), [0, 0, 1]) self.assertEqual(lin[1].sense, Constraint.Sense.GE) self.assertEqual(lin[1].rhs, 1) - self.assertEqual(lin[1].name, 'c2') - self.assertEqual(q_p.get_linear_constraint(1).name, 'c2') - self.assertEqual(q_p.get_linear_constraint('c2').name, 'c2') + self.assertEqual(lin[1].name, "c2") + self.assertEqual(q_p.get_linear_constraint(1).name, "c2") + self.assertEqual(q_p.get_linear_constraint("c2").name, "c2") with self.assertRaises(KeyError): - q_p.remove_linear_constraint('c1') + q_p.remove_linear_constraint("c1") with self.assertRaises(IndexError): q_p.remove_linear_constraint(9) - q_p.linear_constraint(sense='E') + q_p.linear_constraint(sense="E") self.assertEqual(q_p.linear_constraints[-1].sense, Constraint.Sense.EQ) - q_p.linear_constraint(sense='G') + q_p.linear_constraint(sense="G") self.assertEqual(q_p.linear_constraints[-1].sense, Constraint.Sense.GE) - q_p.linear_constraint(sense='L') + q_p.linear_constraint(sense="L") self.assertEqual(q_p.linear_constraints[-1].sense, Constraint.Sense.LE) - q_p.linear_constraint(sense='EQ') + q_p.linear_constraint(sense="EQ") self.assertEqual(q_p.linear_constraints[-1].sense, Constraint.Sense.EQ) - q_p.linear_constraint(sense='GE') + q_p.linear_constraint(sense="GE") self.assertEqual(q_p.linear_constraints[-1].sense, Constraint.Sense.GE) - q_p.linear_constraint(sense='LE') + q_p.linear_constraint(sense="LE") self.assertEqual(q_p.linear_constraints[-1].sense, Constraint.Sense.LE) - q_p.linear_constraint(sense='=') + q_p.linear_constraint(sense="=") self.assertEqual(q_p.linear_constraints[-1].sense, Constraint.Sense.EQ) - q_p.linear_constraint(sense='>') + q_p.linear_constraint(sense=">") self.assertEqual(q_p.linear_constraints[-1].sense, Constraint.Sense.GE) - q_p.linear_constraint(sense='<') + q_p.linear_constraint(sense="<") with self.assertRaises(QiskitOptimizationError): - q_p.linear_constraint(sense='=>') + q_p.linear_constraint(sense="=>") def test_quadratic_constraints_handling(self): """test quadratic constraints handling""" q_p = QuadraticProgram() - q_p.binary_var('x') - q_p.binary_var('y') - q_p.binary_var('z') - q_p.quadratic_constraint({'x': 1}, {('x', 'y'): 1}, '==', 1) - q_p.quadratic_constraint({'y': 1}, {('y', 'z'): 1}, '<=', 1) - q_p.quadratic_constraint({'z': 1}, {('z', 'x'): 1}, '>=', 1) + q_p.binary_var("x") + q_p.binary_var("y") + q_p.binary_var("z") + q_p.quadratic_constraint({"x": 1}, {("x", "y"): 1}, "==", 1) + q_p.quadratic_constraint({"y": 1}, {("y", "z"): 1}, "<=", 1) + q_p.quadratic_constraint({"z": 1}, {("z", "x"): 1}, ">=", 1) self.assertEqual(q_p.get_num_quadratic_constraints(), 3) quad = q_p.quadratic_constraints self.assertEqual(len(quad), 3) self.assertDictEqual(quad[0].linear.to_dict(), {0: 1}) - self.assertDictEqual(quad[0].linear.to_dict(use_name=True), {'x': 1}) + self.assertDictEqual(quad[0].linear.to_dict(use_name=True), {"x": 1}) self.assertListEqual(quad[0].linear.to_array().tolist(), [1, 0, 0]) self.assertDictEqual(quad[0].quadratic.to_dict(), {(0, 1): 1}) - self.assertDictEqual(quad[0].quadratic.to_dict(symmetric=True), - {(0, 1): 0.5, (1, 0): 0.5}) - self.assertDictEqual(quad[0].quadratic.to_dict(use_name=True), {('x', 'y'): 1}) - self.assertDictEqual(quad[0].quadratic.to_dict(use_name=True, symmetric=True), - {('x', 'y'): 0.5, ('y', 'x'): 0.5}) - self.assertListEqual(quad[0].quadratic.to_array().tolist(), - [[0, 1, 0], [0, 0, 0], [0, 0, 0]]) - self.assertListEqual(quad[0].quadratic.to_array(symmetric=True).tolist(), - [[0, 0.5, 0], [0.5, 0, 0], [0, 0, 0]]) + self.assertDictEqual( + quad[0].quadratic.to_dict(symmetric=True), {(0, 1): 0.5, (1, 0): 0.5} + ) + self.assertDictEqual(quad[0].quadratic.to_dict(use_name=True), {("x", "y"): 1}) + self.assertDictEqual( + quad[0].quadratic.to_dict(use_name=True, symmetric=True), + {("x", "y"): 0.5, ("y", "x"): 0.5}, + ) + self.assertListEqual( + quad[0].quadratic.to_array().tolist(), [[0, 1, 0], [0, 0, 0], [0, 0, 0]] + ) + self.assertListEqual( + quad[0].quadratic.to_array(symmetric=True).tolist(), + [[0, 0.5, 0], [0.5, 0, 0], [0, 0, 0]], + ) self.assertEqual(quad[0].sense, Constraint.Sense.EQ) self.assertEqual(quad[0].rhs, 1) - self.assertEqual(quad[0].name, 'q0') - self.assertEqual(q_p.get_quadratic_constraint(0).name, 'q0') - self.assertEqual(q_p.get_quadratic_constraint('q0').name, 'q0') + self.assertEqual(quad[0].name, "q0") + self.assertEqual(q_p.get_quadratic_constraint(0).name, "q0") + self.assertEqual(q_p.get_quadratic_constraint("q0").name, "q0") self.assertDictEqual(quad[1].linear.to_dict(), {1: 1}) - self.assertDictEqual(quad[1].linear.to_dict(use_name=True), {'y': 1}) + self.assertDictEqual(quad[1].linear.to_dict(use_name=True), {"y": 1}) self.assertListEqual(quad[1].linear.to_array().tolist(), [0, 1, 0]) self.assertDictEqual(quad[1].quadratic.to_dict(), {(1, 2): 1}) - self.assertDictEqual(quad[1].quadratic.to_dict(symmetric=True), - {(1, 2): 0.5, (2, 1): 0.5}) - self.assertDictEqual(quad[1].quadratic.to_dict(use_name=True), {('y', 'z'): 1}) - self.assertDictEqual(quad[1].quadratic.to_dict(use_name=True, symmetric=True), - {('y', 'z'): 0.5, ('z', 'y'): 0.5}) - self.assertListEqual(quad[1].quadratic.to_array().tolist(), - [[0, 0, 0], [0, 0, 1], [0, 0, 0]]) - self.assertListEqual(quad[1].quadratic.to_array(symmetric=True).tolist(), - [[0, 0, 0], [0, 0, 0.5], [0, 0.5, 0]]) + self.assertDictEqual( + quad[1].quadratic.to_dict(symmetric=True), {(1, 2): 0.5, (2, 1): 0.5} + ) + self.assertDictEqual(quad[1].quadratic.to_dict(use_name=True), {("y", "z"): 1}) + self.assertDictEqual( + quad[1].quadratic.to_dict(use_name=True, symmetric=True), + {("y", "z"): 0.5, ("z", "y"): 0.5}, + ) + self.assertListEqual( + quad[1].quadratic.to_array().tolist(), [[0, 0, 0], [0, 0, 1], [0, 0, 0]] + ) + self.assertListEqual( + quad[1].quadratic.to_array(symmetric=True).tolist(), + [[0, 0, 0], [0, 0, 0.5], [0, 0.5, 0]], + ) self.assertEqual(quad[1].sense, Constraint.Sense.LE) self.assertEqual(quad[1].rhs, 1) - self.assertEqual(quad[1].name, 'q1') - self.assertEqual(q_p.get_quadratic_constraint(1).name, 'q1') - self.assertEqual(q_p.get_quadratic_constraint('q1').name, 'q1') + self.assertEqual(quad[1].name, "q1") + self.assertEqual(q_p.get_quadratic_constraint(1).name, "q1") + self.assertEqual(q_p.get_quadratic_constraint("q1").name, "q1") self.assertDictEqual(quad[2].linear.to_dict(), {2: 1}) - self.assertDictEqual(quad[2].linear.to_dict(use_name=True), {'z': 1}) + self.assertDictEqual(quad[2].linear.to_dict(use_name=True), {"z": 1}) self.assertListEqual(quad[2].linear.to_array().tolist(), [0, 0, 1]) self.assertDictEqual(quad[2].quadratic.to_dict(), {(0, 2): 1}) - self.assertDictEqual(quad[2].quadratic.to_dict(symmetric=True), - {(0, 2): 0.5, (2, 0): 0.5}) - self.assertDictEqual(quad[2].quadratic.to_dict(use_name=True), {('x', 'z'): 1}) - self.assertDictEqual(quad[2].quadratic.to_dict(use_name=True, symmetric=True), - {('x', 'z'): 0.5, ('z', 'x'): 0.5}) - self.assertListEqual(quad[2].quadratic.to_array().tolist(), - [[0, 0, 1], [0, 0, 0], [0, 0, 0]]) - self.assertListEqual(quad[2].quadratic.to_array(symmetric=True).tolist(), - [[0, 0, 0.5], [0, 0, 0], [0.5, 0, 0]]) + self.assertDictEqual( + quad[2].quadratic.to_dict(symmetric=True), {(0, 2): 0.5, (2, 0): 0.5} + ) + self.assertDictEqual(quad[2].quadratic.to_dict(use_name=True), {("x", "z"): 1}) + self.assertDictEqual( + quad[2].quadratic.to_dict(use_name=True, symmetric=True), + {("x", "z"): 0.5, ("z", "x"): 0.5}, + ) + self.assertListEqual( + quad[2].quadratic.to_array().tolist(), [[0, 0, 1], [0, 0, 0], [0, 0, 0]] + ) + self.assertListEqual( + quad[2].quadratic.to_array(symmetric=True).tolist(), + [[0, 0, 0.5], [0, 0, 0], [0.5, 0, 0]], + ) self.assertEqual(quad[2].sense, Constraint.Sense.GE) self.assertEqual(quad[2].rhs, 1) - self.assertEqual(quad[2].name, 'q2') - self.assertEqual(q_p.get_quadratic_constraint(2).name, 'q2') - self.assertEqual(q_p.get_quadratic_constraint('q2').name, 'q2') + self.assertEqual(quad[2].name, "q2") + self.assertEqual(q_p.get_quadratic_constraint(2).name, "q2") + self.assertEqual(q_p.get_quadratic_constraint("q2").name, "q2") with self.assertRaises(QiskitOptimizationError): - q_p.quadratic_constraint(name='q0') + q_p.quadratic_constraint(name="q0") with self.assertRaises(QiskitOptimizationError): - q_p.quadratic_constraint(name='q1') + q_p.quadratic_constraint(name="q1") with self.assertRaises(QiskitOptimizationError): - q_p.quadratic_constraint(name='q2') + q_p.quadratic_constraint(name="q2") with self.assertRaises(IndexError): q_p.get_quadratic_constraint(4) with self.assertRaises(KeyError): - q_p.get_quadratic_constraint('q3') + q_p.get_quadratic_constraint("q3") - q_p.remove_quadratic_constraint('q1') + q_p.remove_quadratic_constraint("q1") quad = q_p.quadratic_constraints self.assertEqual(len(quad), 2) self.assertDictEqual(quad[1].linear.to_dict(), {2: 1}) - self.assertDictEqual(quad[1].linear.to_dict(use_name=True), {'z': 1}) + self.assertDictEqual(quad[1].linear.to_dict(use_name=True), {"z": 1}) self.assertListEqual(quad[1].linear.to_array().tolist(), [0, 0, 1]) self.assertDictEqual(quad[1].quadratic.to_dict(), {(0, 2): 1}) - self.assertDictEqual(quad[1].quadratic.to_dict(symmetric=True), - {(0, 2): 0.5, (2, 0): 0.5}) - self.assertDictEqual(quad[1].quadratic.to_dict(use_name=True), {('x', 'z'): 1}) - self.assertDictEqual(quad[1].quadratic.to_dict(use_name=True, symmetric=True), - {('x', 'z'): 0.5, ('z', 'x'): 0.5}) - self.assertListEqual(quad[1].quadratic.to_array().tolist(), - [[0, 0, 1], [0, 0, 0], [0, 0, 0]]) - self.assertListEqual(quad[1].quadratic.to_array(symmetric=True).tolist(), - [[0, 0, 0.5], [0, 0, 0], [0.5, 0, 0]]) + self.assertDictEqual( + quad[1].quadratic.to_dict(symmetric=True), {(0, 2): 0.5, (2, 0): 0.5} + ) + self.assertDictEqual(quad[1].quadratic.to_dict(use_name=True), {("x", "z"): 1}) + self.assertDictEqual( + quad[1].quadratic.to_dict(use_name=True, symmetric=True), + {("x", "z"): 0.5, ("z", "x"): 0.5}, + ) + self.assertListEqual( + quad[1].quadratic.to_array().tolist(), [[0, 0, 1], [0, 0, 0], [0, 0, 0]] + ) + self.assertListEqual( + quad[1].quadratic.to_array(symmetric=True).tolist(), + [[0, 0, 0.5], [0, 0, 0], [0.5, 0, 0]], + ) self.assertEqual(quad[1].sense, Constraint.Sense.GE) self.assertEqual(quad[1].rhs, 1) - self.assertEqual(quad[1].name, 'q2') - self.assertEqual(q_p.get_quadratic_constraint(1).name, 'q2') - self.assertEqual(q_p.get_quadratic_constraint('q2').name, 'q2') + self.assertEqual(quad[1].name, "q2") + self.assertEqual(q_p.get_quadratic_constraint(1).name, "q2") + self.assertEqual(q_p.get_quadratic_constraint("q2").name, "q2") with self.assertRaises(KeyError): - q_p.remove_quadratic_constraint('q1') + q_p.remove_quadratic_constraint("q1") with self.assertRaises(IndexError): q_p.remove_quadratic_constraint(9) def test_objective_handling(self): """test objective handling""" q_p = QuadraticProgram() - q_p.binary_var('x') - q_p.binary_var('y') - q_p.binary_var('z') + q_p.binary_var("x") + q_p.binary_var("y") + q_p.binary_var("z") q_p.minimize() obj = q_p.objective self.assertEqual(obj.sense, QuadraticObjective.Sense.MINIMIZE) self.assertEqual(obj.constant, 0) self.assertDictEqual(obj.linear.to_dict(), {}) self.assertDictEqual(obj.quadratic.to_dict(), {}) - q_p.maximize(1, {'y': 1}, {('z', 'x'): 1, ('y', 'y'): 1}) + q_p.maximize(1, {"y": 1}, {("z", "x"): 1, ("y", "y"): 1}) obj = q_p.objective self.assertEqual(obj.sense, QuadraticObjective.Sense.MAXIMIZE) self.assertEqual(obj.constant, 1) self.assertDictEqual(obj.linear.to_dict(), {1: 1}) - self.assertDictEqual(obj.linear.to_dict(use_name=True), {'y': 1}) + self.assertDictEqual(obj.linear.to_dict(use_name=True), {"y": 1}) self.assertListEqual(obj.linear.to_array().tolist(), [0, 1, 0]) self.assertDictEqual(obj.quadratic.to_dict(), {(0, 2): 1, (1, 1): 1}) - self.assertDictEqual(obj.quadratic.to_dict(symmetric=True), - {(0, 2): 0.5, (2, 0): 0.5, (1, 1): 1}) - self.assertDictEqual(obj.quadratic.to_dict(use_name=True), - {('x', 'z'): 1, ('y', 'y'): 1}) - self.assertDictEqual(obj.quadratic.to_dict(use_name=True, symmetric=True), - {('x', 'z'): 0.5, ('z', 'x'): 0.5, ('y', 'y'): 1}) - self.assertListEqual(obj.quadratic.to_array().tolist(), - [[0, 0, 1], [0, 1, 0], [0, 0, 0]]) - self.assertListEqual(obj.quadratic.to_array(symmetric=True).tolist(), - [[0, 0, 0.5], [0, 1, 0], [0.5, 0, 0]]) + self.assertDictEqual( + obj.quadratic.to_dict(symmetric=True), {(0, 2): 0.5, (2, 0): 0.5, (1, 1): 1} + ) + self.assertDictEqual( + obj.quadratic.to_dict(use_name=True), {("x", "z"): 1, ("y", "y"): 1} + ) + self.assertDictEqual( + obj.quadratic.to_dict(use_name=True, symmetric=True), + {("x", "z"): 0.5, ("z", "x"): 0.5, ("y", "y"): 1}, + ) + self.assertListEqual( + obj.quadratic.to_array().tolist(), [[0, 0, 1], [0, 1, 0], [0, 0, 0]] + ) + self.assertListEqual( + obj.quadratic.to_array(symmetric=True).tolist(), + [[0, 0, 0.5], [0, 1, 0], [0.5, 0, 0]], + ) @requires_extra_library def test_read_from_lp_file(self): @@ -607,13 +749,14 @@ def test_read_from_lp_file(self): try: q_p = QuadraticProgram() with self.assertRaises(FileNotFoundError): - q_p.read_from_lp_file('') + q_p.read_from_lp_file("") with self.assertRaises(FileNotFoundError): - q_p.read_from_lp_file('no_file.txt') - lp_file = self.get_resource_path('test_quadratic_program.lp', - 'problems/resources') + q_p.read_from_lp_file("no_file.txt") + lp_file = self.get_resource_path( + "test_quadratic_program.lp", "problems/resources" + ) q_p.read_from_lp_file(lp_file) - self.assertEqual(q_p.name, 'my problem') + self.assertEqual(q_p.name, "my problem") self.assertEqual(q_p.get_num_vars(), 3) self.assertEqual(q_p.get_num_binary_vars(), 1) self.assertEqual(q_p.get_num_integer_vars(), 1) @@ -621,57 +764,66 @@ def test_read_from_lp_file(self): self.assertEqual(q_p.get_num_linear_constraints(), 3) self.assertEqual(q_p.get_num_quadratic_constraints(), 3) - self.assertEqual(q_p.variables[0].name, 'x') + self.assertEqual(q_p.variables[0].name, "x") self.assertEqual(q_p.variables[0].vartype, Variable.Type.BINARY) self.assertEqual(q_p.variables[0].lowerbound, 0) self.assertEqual(q_p.variables[0].upperbound, 1) - self.assertEqual(q_p.variables[1].name, 'y') + self.assertEqual(q_p.variables[1].name, "y") self.assertEqual(q_p.variables[1].vartype, Variable.Type.INTEGER) self.assertEqual(q_p.variables[1].lowerbound, -1) self.assertEqual(q_p.variables[1].upperbound, 5) - self.assertEqual(q_p.variables[2].name, 'z') + self.assertEqual(q_p.variables[2].name, "z") self.assertEqual(q_p.variables[2].vartype, Variable.Type.CONTINUOUS) self.assertEqual(q_p.variables[2].lowerbound, -1) self.assertEqual(q_p.variables[2].upperbound, 5) self.assertEqual(q_p.objective.sense, QuadraticObjective.Sense.MINIMIZE) self.assertEqual(q_p.objective.constant, 1) - self.assertDictEqual(q_p.objective.linear.to_dict(use_name=True), - {'x': 1, 'y': -1, 'z': 10}) - self.assertDictEqual(q_p.objective.quadratic.to_dict(use_name=True), - {('x', 'x'): 0.5, ('y', 'z'): -1}) + self.assertDictEqual( + q_p.objective.linear.to_dict(use_name=True), {"x": 1, "y": -1, "z": 10} + ) + self.assertDictEqual( + q_p.objective.quadratic.to_dict(use_name=True), + {("x", "x"): 0.5, ("y", "z"): -1}, + ) cst = q_p.linear_constraints - self.assertEqual(cst[0].name, 'lin_eq') - self.assertDictEqual(cst[0].linear.to_dict(use_name=True), {'x': 1, 'y': 2}) + self.assertEqual(cst[0].name, "lin_eq") + self.assertDictEqual(cst[0].linear.to_dict(use_name=True), {"x": 1, "y": 2}) self.assertEqual(cst[0].sense, Constraint.Sense.EQ) self.assertEqual(cst[0].rhs, 1) - self.assertEqual(cst[1].name, 'lin_leq') - self.assertDictEqual(cst[1].linear.to_dict(use_name=True), {'x': 1, 'y': 2}) + self.assertEqual(cst[1].name, "lin_leq") + self.assertDictEqual(cst[1].linear.to_dict(use_name=True), {"x": 1, "y": 2}) self.assertEqual(cst[1].sense, Constraint.Sense.LE) self.assertEqual(cst[1].rhs, 1) - self.assertEqual(cst[2].name, 'lin_geq') - self.assertDictEqual(cst[2].linear.to_dict(use_name=True), {'x': 1, 'y': 2}) + self.assertEqual(cst[2].name, "lin_geq") + self.assertDictEqual(cst[2].linear.to_dict(use_name=True), {"x": 1, "y": 2}) self.assertEqual(cst[2].sense, Constraint.Sense.GE) self.assertEqual(cst[2].rhs, 1) cst = q_p.quadratic_constraints - self.assertEqual(cst[0].name, 'quad_eq') - self.assertDictEqual(cst[0].linear.to_dict(use_name=True), {'x': 1, 'y': 1}) - self.assertDictEqual(cst[0].quadratic.to_dict(use_name=True), - {('x', 'x'): 1, ('y', 'z'): -1, ('z', 'z'): 2}) + self.assertEqual(cst[0].name, "quad_eq") + self.assertDictEqual(cst[0].linear.to_dict(use_name=True), {"x": 1, "y": 1}) + self.assertDictEqual( + cst[0].quadratic.to_dict(use_name=True), + {("x", "x"): 1, ("y", "z"): -1, ("z", "z"): 2}, + ) self.assertEqual(cst[0].sense, Constraint.Sense.EQ) self.assertEqual(cst[0].rhs, 1) - self.assertEqual(cst[1].name, 'quad_leq') - self.assertDictEqual(cst[1].linear.to_dict(use_name=True), {'x': 1, 'y': 1}) - self.assertDictEqual(cst[1].quadratic.to_dict(use_name=True), - {('x', 'x'): 1, ('y', 'z'): -1, ('z', 'z'): 2}) + self.assertEqual(cst[1].name, "quad_leq") + self.assertDictEqual(cst[1].linear.to_dict(use_name=True), {"x": 1, "y": 1}) + self.assertDictEqual( + cst[1].quadratic.to_dict(use_name=True), + {("x", "x"): 1, ("y", "z"): -1, ("z", "z"): 2}, + ) self.assertEqual(cst[1].sense, Constraint.Sense.LE) self.assertEqual(cst[1].rhs, 1) - self.assertEqual(cst[2].name, 'quad_geq') - self.assertDictEqual(cst[2].linear.to_dict(use_name=True), {'x': 1, 'y': 1}) - self.assertDictEqual(cst[2].quadratic.to_dict(use_name=True), - {('x', 'x'): 1, ('y', 'z'): -1, ('z', 'z'): 2}) + self.assertEqual(cst[2].name, "quad_geq") + self.assertDictEqual(cst[2].linear.to_dict(use_name=True), {"x": 1, "y": 1}) + self.assertDictEqual( + cst[2].quadratic.to_dict(use_name=True), + {("x", "x"): 1, ("y", "z"): -1, ("z", "z"): 2}, + ) self.assertEqual(cst[2].sense, Constraint.Sense.GE) self.assertEqual(cst[2].rhs, 1) except RuntimeError as ex: @@ -679,24 +831,40 @@ def test_read_from_lp_file(self): def test_write_to_lp_file(self): """test write problem""" - q_p = QuadraticProgram('my problem') - q_p.binary_var('x') - q_p.integer_var(-1, 5, 'y') - q_p.continuous_var(-1, 5, 'z') - q_p.minimize(1, {'x': 1, 'y': -1, 'z': 10}, {('x', 'x'): 0.5, ('y', 'z'): -1}) - q_p.linear_constraint({'x': 1, 'y': 2}, '==', 1, 'lin_eq') - q_p.linear_constraint({'x': 1, 'y': 2}, '<=', 1, 'lin_leq') - q_p.linear_constraint({'x': 1, 'y': 2}, '>=', 1, 'lin_geq') - q_p.quadratic_constraint({'x': 1, 'y': 1}, {('x', 'x'): 1, ('y', 'z'): -1, ('z', 'z'): 2}, - '==', 1, 'quad_eq') - q_p.quadratic_constraint({'x': 1, 'y': 1}, {('x', 'x'): 1, ('y', 'z'): -1, ('z', 'z'): 2}, - '<=', 1, 'quad_leq') - q_p.quadratic_constraint({'x': 1, 'y': 1}, {('x', 'x'): 1, ('y', 'z'): -1, ('z', 'z'): 2}, - '>=', 1, 'quad_geq') - - reference_file_name = self.get_resource_path('test_quadratic_program.lp', - 'problems/resources') - with tempfile.NamedTemporaryFile(mode='w+t', suffix='.lp') as temp_output_file: + q_p = QuadraticProgram("my problem") + q_p.binary_var("x") + q_p.integer_var(-1, 5, "y") + q_p.continuous_var(-1, 5, "z") + q_p.minimize(1, {"x": 1, "y": -1, "z": 10}, {("x", "x"): 0.5, ("y", "z"): -1}) + q_p.linear_constraint({"x": 1, "y": 2}, "==", 1, "lin_eq") + q_p.linear_constraint({"x": 1, "y": 2}, "<=", 1, "lin_leq") + q_p.linear_constraint({"x": 1, "y": 2}, ">=", 1, "lin_geq") + q_p.quadratic_constraint( + {"x": 1, "y": 1}, + {("x", "x"): 1, ("y", "z"): -1, ("z", "z"): 2}, + "==", + 1, + "quad_eq", + ) + q_p.quadratic_constraint( + {"x": 1, "y": 1}, + {("x", "x"): 1, ("y", "z"): -1, ("z", "z"): 2}, + "<=", + 1, + "quad_leq", + ) + q_p.quadratic_constraint( + {"x": 1, "y": 1}, + {("x", "x"): 1, ("y", "z"): -1, ("z", "z"): 2}, + ">=", + 1, + "quad_geq", + ) + + reference_file_name = self.get_resource_path( + "test_quadratic_program.lp", "problems/resources" + ) + with tempfile.NamedTemporaryFile(mode="w+t", suffix=".lp") as temp_output_file: q_p.write_to_lp_file(temp_output_file.name) with open(reference_file_name) as reference: lines1 = temp_output_file.readlines() @@ -705,70 +873,74 @@ def test_write_to_lp_file(self): with tempfile.TemporaryDirectory() as temp_problem_dir: q_p.write_to_lp_file(temp_problem_dir) - with open(path.join(temp_problem_dir, 'my_problem.lp')) as file1, open( - reference_file_name) as file2: + with open(path.join(temp_problem_dir, "my_problem.lp")) as file1, open( + reference_file_name + ) as file2: lines1 = file1.readlines() lines2 = file2.readlines() self.assertListEqual(lines1, lines2) with self.assertRaises(OSError): - q_p.write_to_lp_file('/cannot/write/this/file.lp') + q_p.write_to_lp_file("/cannot/write/this/file.lp") with self.assertRaises(DOcplexException): - q_p.write_to_lp_file('') + q_p.write_to_lp_file("") def test_docplex(self): """test from_docplex and to_docplex""" - q_p = QuadraticProgram('test') - q_p.binary_var(name='x') - q_p.integer_var(name='y', lowerbound=-2, upperbound=4) - q_p.continuous_var(name='z', lowerbound=-1.5, upperbound=3.2) - q_p.minimize(constant=1, linear={'x': 1, 'y': 2}, - quadratic={('x', 'y'): -1, ('z', 'z'): 2}) - q_p.linear_constraint({'x': 2, 'z': -1}, '==', 1) - q_p.quadratic_constraint({'x': 2, 'z': -1}, {('y', 'z'): 3}, '==', 1) + q_p = QuadraticProgram("test") + q_p.binary_var(name="x") + q_p.integer_var(name="y", lowerbound=-2, upperbound=4) + q_p.continuous_var(name="z", lowerbound=-1.5, upperbound=3.2) + q_p.minimize( + constant=1, + linear={"x": 1, "y": 2}, + quadratic={("x", "y"): -1, ("z", "z"): 2}, + ) + q_p.linear_constraint({"x": 2, "z": -1}, "==", 1) + q_p.quadratic_constraint({"x": 2, "z": -1}, {("y", "z"): 3}, "==", 1) q_p2 = QuadraticProgram() q_p2.from_docplex(q_p.to_docplex()) self.assertEqual(q_p.export_as_lp_string(), q_p2.export_as_lp_string()) - mod = Model('test') - x = mod.binary_var('x') - y = mod.integer_var(-2, 4, 'y') - z = mod.continuous_var(-1.5, 3.2, 'z') + mod = Model("test") + x = mod.binary_var("x") + y = mod.integer_var(-2, 4, "y") + z = mod.continuous_var(-1.5, 3.2, "z") mod.minimize(1 + x + 2 * y - x * y + 2 * z * z) - mod.add(2 * x - z == 1, 'c0') - mod.add(2 * x - z + 3 * y * z == 1, 'q0') + mod.add(2 * x - z == 1, "c0") + mod.add(2 * x - z + 3 * y * z == 1, "q0") self.assertEqual(q_p.export_as_lp_string(), mod.export_as_lp_string()) with self.assertRaises(QiskitOptimizationError): mod = Model() - mod.semiinteger_var(lb=1, name='x') + mod.semiinteger_var(lb=1, name="x") q_p.from_docplex(mod) with self.assertRaises(QiskitOptimizationError): mod = Model() - x = mod.binary_var('x') + x = mod.binary_var("x") mod.add_range(0, 2 * x, 1) q_p.from_docplex(mod) with self.assertRaises(QiskitOptimizationError): mod = Model() - x = mod.binary_var('x') - y = mod.binary_var('y') + x = mod.binary_var("x") + y = mod.binary_var("y") mod.add_indicator(x, x + y <= 1, 1) q_p.from_docplex(mod) with self.assertRaises(QiskitOptimizationError): mod = Model() - x = mod.binary_var('x') - y = mod.binary_var('y') + x = mod.binary_var("x") + y = mod.binary_var("y") mod.add_equivalence(x, x + y <= 1, 1) q_p.from_docplex(mod) with self.assertRaises(QiskitOptimizationError): mod = Model() - x = mod.binary_var('x') - y = mod.binary_var('y') + x = mod.binary_var("x") + y = mod.binary_var("y") mod.add(mod.not_equal_constraint(x, y + 1)) q_p.from_docplex(mod) @@ -787,108 +959,125 @@ def test_docplex(self): q_p = QuadraticProgram() q_p.from_docplex(mod) var_names = [v.name for v in q_p.variables] - self.assertListEqual(var_names, ['x0', 'x1', 'x2']) + self.assertListEqual(var_names, ["x0", "x1", "x2"]) senses = [Constraint.Sense.EQ, Constraint.Sense.GE, Constraint.Sense.LE] for i, c in enumerate(q_p.linear_constraints): - self.assertDictEqual(c.linear.to_dict(use_name=True), {'x0': 1, 'x1': 1, 'x2': -1}) + self.assertDictEqual( + c.linear.to_dict(use_name=True), {"x0": 1, "x1": 1, "x2": -1} + ) self.assertEqual(c.rhs, 0) self.assertEqual(c.sense, senses[i]) for i, c in enumerate(q_p.quadratic_constraints): self.assertEqual(c.rhs, 0) - self.assertDictEqual(c.linear.to_dict(use_name=True), {'x2': -1}) - self.assertDictEqual(c.quadratic.to_dict(use_name=True), {('x0', 'x1'): 1}) + self.assertDictEqual(c.linear.to_dict(use_name=True), {"x2": -1}) + self.assertDictEqual(c.quadratic.to_dict(use_name=True), {("x0", "x1"): 1}) self.assertEqual(c.sense, senses[i]) def test_substitute_variables(self): """test substitute variables""" - q_p = QuadraticProgram('test') - q_p.binary_var(name='x') - q_p.integer_var(name='y', lowerbound=-2, upperbound=4) - q_p.continuous_var(name='z', lowerbound=-1.5, upperbound=3.2) - q_p.minimize(constant=1, linear={'x': 1, 'y': 2}, - quadratic={('x', 'y'): -1, ('z', 'z'): 2}) - q_p.linear_constraint({'x': 2, 'z': -1}, '==', 1) - q_p.quadratic_constraint({'x': 2, 'z': -1}, {('y', 'z'): 3}, '<=', -1) - - q_p2 = q_p.substitute_variables(constants={'x': -1}) + q_p = QuadraticProgram("test") + q_p.binary_var(name="x") + q_p.integer_var(name="y", lowerbound=-2, upperbound=4) + q_p.continuous_var(name="z", lowerbound=-1.5, upperbound=3.2) + q_p.minimize( + constant=1, + linear={"x": 1, "y": 2}, + quadratic={("x", "y"): -1, ("z", "z"): 2}, + ) + q_p.linear_constraint({"x": 2, "z": -1}, "==", 1) + q_p.quadratic_constraint({"x": 2, "z": -1}, {("y", "z"): 3}, "<=", -1) + + q_p2 = q_p.substitute_variables(constants={"x": -1}) self.assertEqual(q_p2.status, QuadraticProgram.Status.INFEASIBLE) - q_p2 = q_p.substitute_variables(constants={'y': -3}) + q_p2 = q_p.substitute_variables(constants={"y": -3}) self.assertEqual(q_p2.status, QuadraticProgram.Status.INFEASIBLE) - q_p2 = q_p.substitute_variables(constants={'x': 1, 'z': 2}) + q_p2 = q_p.substitute_variables(constants={"x": 1, "z": 2}) self.assertEqual(q_p2.status, QuadraticProgram.Status.INFEASIBLE) q_p2.clear() self.assertEqual(q_p2.status, QuadraticProgram.Status.VALID) - q_p2 = q_p.substitute_variables(constants={'x': 0}) + q_p2 = q_p.substitute_variables(constants={"x": 0}) self.assertEqual(q_p2.status, QuadraticProgram.Status.VALID) - self.assertDictEqual(q_p2.objective.linear.to_dict(use_name=True), {'y': 2}) - self.assertDictEqual(q_p2.objective.quadratic.to_dict(use_name=True), {('z', 'z'): 2}) + self.assertDictEqual(q_p2.objective.linear.to_dict(use_name=True), {"y": 2}) + self.assertDictEqual( + q_p2.objective.quadratic.to_dict(use_name=True), {("z", "z"): 2} + ) self.assertEqual(q_p2.objective.constant, 1) self.assertEqual(len(q_p2.linear_constraints), 1) self.assertEqual(len(q_p2.quadratic_constraints), 1) cst = q_p2.linear_constraints[0] - self.assertDictEqual(cst.linear.to_dict(use_name=True), {'z': -1}) - self.assertEqual(cst.sense.name, 'EQ') + self.assertDictEqual(cst.linear.to_dict(use_name=True), {"z": -1}) + self.assertEqual(cst.sense.name, "EQ") self.assertEqual(cst.rhs, 1) cst = q_p2.quadratic_constraints[0] - self.assertDictEqual(cst.linear.to_dict(use_name=True), {'z': -1}) - self.assertDictEqual(cst.quadratic.to_dict(use_name=True), {('y', 'z'): 3}) - self.assertEqual(cst.sense.name, 'LE') + self.assertDictEqual(cst.linear.to_dict(use_name=True), {"z": -1}) + self.assertDictEqual(cst.quadratic.to_dict(use_name=True), {("y", "z"): 3}) + self.assertEqual(cst.sense.name, "LE") self.assertEqual(cst.rhs, -1) - q_p2 = q_p.substitute_variables(constants={'z': -1}) + q_p2 = q_p.substitute_variables(constants={"z": -1}) self.assertEqual(q_p2.status, QuadraticProgram.Status.VALID) - self.assertDictEqual(q_p2.objective.linear.to_dict(use_name=True), {'x': 1, 'y': 2}) - self.assertDictEqual(q_p2.objective.quadratic.to_dict(use_name=True), {('x', 'y'): -1}) + self.assertDictEqual( + q_p2.objective.linear.to_dict(use_name=True), {"x": 1, "y": 2} + ) + self.assertDictEqual( + q_p2.objective.quadratic.to_dict(use_name=True), {("x", "y"): -1} + ) self.assertEqual(q_p2.objective.constant, 3) self.assertEqual(len(q_p2.linear_constraints), 2) self.assertEqual(len(q_p2.quadratic_constraints), 0) cst = q_p2.linear_constraints[0] - self.assertDictEqual(cst.linear.to_dict(use_name=True), {'x': 2}) - self.assertEqual(cst.sense.name, 'EQ') + self.assertDictEqual(cst.linear.to_dict(use_name=True), {"x": 2}) + self.assertEqual(cst.sense.name, "EQ") self.assertEqual(cst.rhs, 0) cst = q_p2.linear_constraints[1] - self.assertDictEqual(cst.linear.to_dict(use_name=True), {'x': 2, 'y': -3}) - self.assertEqual(cst.sense.name, 'LE') + self.assertDictEqual(cst.linear.to_dict(use_name=True), {"x": 2, "y": -3}) + self.assertEqual(cst.sense.name, "LE") self.assertEqual(cst.rhs, -2) - q_p2 = q_p.substitute_variables(variables={'y': ('x', -0.5)}) + q_p2 = q_p.substitute_variables(variables={"y": ("x", -0.5)}) self.assertEqual(q_p2.status, QuadraticProgram.Status.VALID) self.assertDictEqual(q_p2.objective.linear.to_dict(use_name=True), {}) - self.assertDictEqual(q_p2.objective.quadratic.to_dict(use_name=True), - {('x', 'x'): 0.5, ('z', 'z'): 2}) + self.assertDictEqual( + q_p2.objective.quadratic.to_dict(use_name=True), + {("x", "x"): 0.5, ("z", "z"): 2}, + ) self.assertEqual(q_p2.objective.constant, 1) self.assertEqual(len(q_p2.linear_constraints), 1) self.assertEqual(len(q_p2.quadratic_constraints), 1) cst = q_p2.linear_constraints[0] - self.assertDictEqual(cst.linear.to_dict(use_name=True), {'x': 2, 'z': -1}) - self.assertEqual(cst.sense.name, 'EQ') + self.assertDictEqual(cst.linear.to_dict(use_name=True), {"x": 2, "z": -1}) + self.assertEqual(cst.sense.name, "EQ") self.assertEqual(cst.rhs, 1) cst = q_p2.quadratic_constraints[0] - self.assertDictEqual(cst.linear.to_dict(use_name=True), {'x': 2, 'z': -1}) - self.assertDictEqual(cst.quadratic.to_dict(use_name=True), {('x', 'z'): -1.5}) - self.assertEqual(cst.sense.name, 'LE') + self.assertDictEqual(cst.linear.to_dict(use_name=True), {"x": 2, "z": -1}) + self.assertDictEqual(cst.quadratic.to_dict(use_name=True), {("x", "z"): -1.5}) + self.assertEqual(cst.sense.name, "LE") self.assertEqual(cst.rhs, -1) def test_feasibility(self): """Tests feasibility methods.""" - mod = Model('test') + mod = Model("test") # 0, 5 - x = mod.continuous_var(-1, 1, 'x', ) - y = mod.continuous_var(-10, 10, 'y') + x = mod.continuous_var( + -1, + 1, + "x", + ) + y = mod.continuous_var(-10, 10, "y") mod.minimize(x + y) - mod.add(x + y <= 10, 'c0') - mod.add(x + y >= -10, 'c1') - mod.add(x + y == 5, 'c2') - mod.add(x * x + y <= 10, 'c3') - mod.add(x * x + y >= 5, 'c4') - mod.add(x * x + y * y == 25, 'c5') + mod.add(x + y <= 10, "c0") + mod.add(x + y >= -10, "c1") + mod.add(x + y == 5, "c2") + mod.add(x * x + y <= 10, "c3") + mod.add(x * x + y >= 5, "c4") + mod.add(x * x + y * y == 25, "c5") q_p = QuadraticProgram() q_p.from_docplex(mod) @@ -904,14 +1093,14 @@ def test_feasibility(self): self.assertFalse(feasible) self.assertIsNotNone(variables) self.assertEqual(1, len(variables)) - self.assertEqual('x', variables[0].name) + self.assertEqual("x", variables[0].name) self.assertIsNotNone(constraints) self.assertEqual(3, len(constraints)) - self.assertEqual('c2', constraints[0].name) - self.assertEqual('c3', constraints[1].name) - self.assertEqual('c5', constraints[2].name) + self.assertEqual("c2", constraints[0].name) + self.assertEqual("c3", constraints[1].name) + self.assertEqual("c5", constraints[2].name) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/problems/test_variable.py b/test/problems/test_variable.py index c1cfb26f9..600b5bcc3 100644 --- a/test/problems/test_variable.py +++ b/test/problems/test_variable.py @@ -23,10 +23,10 @@ class TestVariable(QiskitOptimizationTestCase): """Test Variable.""" def test_init(self): - """ test init """ + """test init""" quadratic_program = QuadraticProgram() - name = 'variable' + name = "variable" lowerbound = 0 upperbound = 10 vartype = Variable.Type.INTEGER @@ -39,10 +39,10 @@ def test_init(self): self.assertEqual(variable.vartype, Variable.Type.INTEGER) def test_init_default(self): - """ test init with default values.""" + """test init with default values.""" quadratic_program = QuadraticProgram() - name = 'variable' + name = "variable" variable = Variable(quadratic_program, name) @@ -52,5 +52,5 @@ def test_init_default(self): self.assertEqual(variable.vartype, Variable.Type.CONTINUOUS) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/test/test_readme_sample.py b/test/test_readme_sample.py index f51bfb401..9fd0897db 100644 --- a/test/test_readme_sample.py +++ b/test/test_readme_sample.py @@ -27,9 +27,8 @@ class TestReadmeSample(QiskitOptimizationTestCase): """Test sample code from readme""" def _sample_code(self): - def print(*args): - """ overloads print to log values """ + """overloads print to log values""" if args: self.log.debug(args[0], *args[1:]) @@ -57,17 +56,19 @@ def print(*args): # Formulate the problem as quadratic program problem = QuadraticProgram() - _ = [problem.binary_var('x{}'.format(i)) for i in range(n)] # create n binary variables + _ = [ + problem.binary_var("x{}".format(i)) for i in range(n) + ] # create n binary variables linear = w.dot(np.ones(n)) quadratic = -w problem.maximize(linear=linear, quadratic=quadratic) # Fix node 0 to be 1 to break the symmetry of the max-cut solution - problem.linear_constraint([1, 0, 0, 0], '==', 1) + problem.linear_constraint([1, 0, 0, 0], "==", 1) # Run quantum algorithm QAOA on qasm simulator spsa = SPSA(maxiter=250) - backend = BasicAer.get_backend('qasm_simulator') + backend = BasicAer.get_backend("qasm_simulator") qaoa = QAOA(optimizer=spsa, reps=5, quantum_instance=backend) algorithm = MinimumEigenOptimizer(qaoa) result = algorithm.solve(problem) @@ -77,15 +78,16 @@ def print(*args): return result def test_readme_sample(self): - """ readme sample test """ + """readme sample test""" - print('') + print("") import numpy as np + # for now do this until test is fixed msg = None for idx in range(3): try: - print(f'Trial number {idx+1}') + print(f"Trial number {idx+1}") # Fix the random seed of SPSA (Optional) algorithm_globals.random_seed = 123 result = self._sample_code() @@ -100,5 +102,5 @@ def test_readme_sample(self): self.skipTest(msg) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tools/check_copyright.py b/tools/check_copyright.py index c8ed91f64..d5d7a570f 100644 --- a/tools/check_copyright.py +++ b/tools/check_copyright.py @@ -22,19 +22,21 @@ class CopyrightChecker: - """ Check copyright """ + """Check copyright""" - _UTF_STRING = '# -*- coding: utf-8 -*-' - _COPYRIGHT_STRING = '# (C) Copyright IBM ' + _UTF_STRING = "# -*- coding: utf-8 -*-" + _COPYRIGHT_STRING = "# (C) Copyright IBM " def __init__(self, root_dir: str) -> None: self._root_dir = root_dir @staticmethod def _exception_to_string(excp: Exception) -> str: - stack = traceback.extract_stack()[:-3] + traceback.extract_tb(excp.__traceback__) + stack = traceback.extract_stack()[:-3] + traceback.extract_tb( + excp.__traceback__ + ) pretty = traceback.format_list(stack) - return ''.join(pretty) + '\n {} {}'.format(excp.__class__, excp) + return "".join(pretty) + "\n {} {}".format(excp.__class__, excp) @staticmethod def _get_year_from_date(date) -> int: @@ -45,29 +47,33 @@ def _get_year_from_date(date) -> int: @staticmethod def _format_output(out: bytes, err: bytes) -> Tuple[int, Union[None, str]]: - out_str = out.decode('utf-8').strip() - err_str = err.decode('utf-8').strip() + out_str = out.decode("utf-8").strip() + err_str = err.decode("utf-8").strip() err_str = err_str if err_str else None year = CopyrightChecker._get_year_from_date(out_str) return year, err_str - def _process_file_last_year(self, relative_path: str) -> Tuple[int, Union[None, str]]: + def _process_file_last_year( + self, relative_path: str + ) -> Tuple[int, Union[None, str]]: # construct minimal environment env = {} - for k in ['SYSTEMROOT', 'PATH']: + for k in ["SYSTEMROOT", "PATH"]: v = os.environ.get(k) if v is not None: env[k] = v # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - with subprocess.Popen(['git', 'log', '-1', '--format=%aI', relative_path], - cwd=self._root_dir, - env=env, - stdin=subprocess.DEVNULL, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) as popen: + env["LANGUAGE"] = "C" + env["LANG"] = "C" + env["LC_ALL"] = "C" + with subprocess.Popen( + ["git", "log", "-1", "--format=%aI", relative_path], + cwd=self._root_dir, + env=env, + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) as popen: out, err = popen.communicate() popen.wait() return CopyrightChecker._format_output(out, err) @@ -83,18 +89,18 @@ def _get_file_last_year(self, relative_path: str) -> int: errors.append("'{}' Last year: {}".format(relative_path, str(ex))) if errors: - raise ValueError(' - '.join(errors)) + raise ValueError(" - ".join(errors)) return last_year def check_copyright(self, file_path) -> Tuple[bool, bool, bool]: - """ check copyright for a file """ + """check copyright for a file""" now = datetime.datetime.now() file_with_utf8 = False file_with_invalid_year = False file_has_header = False try: - with open(file_path, 'rt', encoding="utf8") as file: + with open(file_path, "rt", encoding="utf8") as file: for line in file: relative_path = os.path.relpath(file_path, self._root_dir) if line.startswith(CopyrightChecker._UTF_STRING): @@ -107,8 +113,8 @@ def check_copyright(self, file_path) -> Tuple[bool, bool, bool]: file_has_header = True curr_years = [] for word in line.strip().split(): - for year in word.strip().split(','): - if year.startswith('20') and len(year) >= 4: + for year in word.strip().split(","): + if year.startswith("20") and len(year) >= 4: try: curr_years.append(int(year[0:4])) except ValueError: @@ -124,13 +130,16 @@ def check_copyright(self, file_path) -> Tuple[bool, bool, bool]: last_year = self._get_file_last_year(relative_path) if last_year and header_last_year != last_year: - new_line = '# (C) Copyright IBM ' + new_line = "# (C) Copyright IBM " if header_start_year and header_start_year != last_year: - new_line += '{}, '.format(header_start_year) + new_line += "{}, ".format(header_start_year) - new_line += '{}.\n'.format(now.year) - print("Wrong Copyright Year: '{}': Current: '{}' Correct: '{}'".format( - relative_path, line[:-1], new_line[:-1])) + new_line += "{}.\n".format(now.year) + print( + "Wrong Copyright Year: '{}': Current: '{}' Correct: '{}'".format( + relative_path, line[:-1], new_line[:-1] + ) + ) file_with_invalid_year = True @@ -142,7 +151,7 @@ def check_copyright(self, file_path) -> Tuple[bool, bool, bool]: return file_with_utf8, file_with_invalid_year, file_has_header def check(self) -> Tuple[int, int, int]: - """ check copyright """ + """check copyright""" return self._check(self._root_dir) def _check(self, path: str) -> Tuple[int, int, int]: @@ -152,7 +161,7 @@ def _check(self, path: str) -> Tuple[int, int, int]: for item in os.listdir(path): fullpath = os.path.join(path, item) if os.path.isdir(fullpath): - if not item.startswith('.'): + if not item.startswith("."): files = self._check(fullpath) files_with_utf8 += files[0] files_with_invalid_year += files[1] @@ -161,8 +170,11 @@ def _check(self, path: str) -> Tuple[int, int, int]: if os.path.isfile(fullpath): # check copyright year - file_with_utf8, file_with_invalid_year, file_has_header = \ - self.check_copyright(fullpath) + ( + file_with_utf8, + file_with_invalid_year, + file_has_header, + ) = self.check_copyright(fullpath) if file_with_utf8: files_with_utf8 += 1 if file_with_invalid_year: @@ -174,19 +186,18 @@ def _check(self, path: str) -> Tuple[int, int, int]: def check_path(path): - """ valid path argument """ + """valid path argument""" if not path or os.path.isdir(path): return path raise argparse.ArgumentTypeError("readable_dir:{} is not a valid path".format(path)) -if __name__ == '__main__': - PARSER = argparse.ArgumentParser(description='Check Copyright Tool') - PARSER.add_argument('-path', - type=check_path, - metavar='path', - help='Root path of project.') +if __name__ == "__main__": + PARSER = argparse.ArgumentParser(description="Check Copyright Tool") + PARSER.add_argument( + "-path", type=check_path, metavar="path", help="Root path of project." + ) ARGS = PARSER.parse_args() if not ARGS.path: @@ -195,7 +206,10 @@ def check_path(path): ARGS.path = os.path.abspath(os.path.realpath(os.path.expanduser(ARGS.path))) INVALID_UTF8, INVALID_YEAR, HAS_HEADER = CopyrightChecker(ARGS.path).check() print("{} files have utf8 headers.".format(INVALID_UTF8)) - print("{} of {} files with copyright header have wrong years.".format( - INVALID_YEAR, HAS_HEADER)) + print( + "{} of {} files with copyright header have wrong years.".format( + INVALID_YEAR, HAS_HEADER + ) + ) sys.exit(os.EX_OK if INVALID_UTF8 == 0 and INVALID_YEAR == 0 else os.EX_SOFTWARE) diff --git a/tools/extract_deprecation.py b/tools/extract_deprecation.py index 7fef48d31..f762b8e45 100644 --- a/tools/extract_deprecation.py +++ b/tools/extract_deprecation.py @@ -19,7 +19,7 @@ class DeprecationExtractor: - """ Extract deprecation messages """ + """Extract deprecation messages""" def __init__(self, in_file: str, out_file: str) -> None: self._input_filename = in_file @@ -35,9 +35,9 @@ def extract_messages(self) -> bool: self._messages = None messages = set() - with open(self._input_filename, 'rt', encoding="utf8", errors='ignore') as file: + with open(self._input_filename, "rt", encoding="utf8", errors="ignore") as file: for line in file: - if line.find('DeprecationWarning:') > 0: + if line.find("DeprecationWarning:") > 0: messages.add(line.strip()) if messages: @@ -57,19 +57,19 @@ def save_to_output(self, force_create: bool) -> bool: if self._output_filename: # create file even if it is empty if self._messages or force_create: - with open(self._output_filename, 'w') as file: + with open(self._output_filename, "w") as file: if self._messages: - file.write('\n'.join(self._messages)) + file.write("\n".join(self._messages)) return True return False def print_messages(self) -> None: - """ print messages """ + """print messages""" if self._messages: - print('---------------------') - print('Deprecation Messages:') - print('---------------------') + print("---------------------") + print("Deprecation Messages:") + print("---------------------") for line in self._messages: print(line) @@ -81,16 +81,14 @@ def _check_file(path) -> str: return path -if __name__ == '__main__': - PARSER = argparse.ArgumentParser(description='Qiskit Extract Deprecation Messages Tool') - PARSER.add_argument('-file', - type=_check_file, - required=True, - metavar='file', - help='Input file.') - PARSER.add_argument('-output', - metavar='output', - help='Output file.') +if __name__ == "__main__": + PARSER = argparse.ArgumentParser( + description="Qiskit Extract Deprecation Messages Tool" + ) + PARSER.add_argument( + "-file", type=_check_file, required=True, metavar="file", help="Input file." + ) + PARSER.add_argument("-output", metavar="output", help="Output file.") ARGS = PARSER.parse_args() diff --git a/tox.ini b/tox.ini index 29c9622a1..d007e8ba7 100644 --- a/tox.ini +++ b/tox.ini @@ -18,13 +18,18 @@ commands = stestr run {posargs} [testenv:lint] +envdir = .tox/lint basepython = python3 commands = - pycodestyle qiskit_optimization test tools + black --check {posargs} qiskit_optimization test tools pylint -rn qiskit_optimization test tools mypy qiskit_optimization test tools python3 {toxinidir}/tools/check_copyright.py -path {toxinidir} +[testenv:black] +envdir = .tox/lint +commands = black {posargs} qiskit_optimization test tools + [testenv:coverage] basepython = python3 setenv = @@ -41,6 +46,7 @@ commands = sphinx-build -b html {posargs} docs/ docs/_build/html [pycodestyle] -# default ignores + E741 -ignore = E121, E123, E126, E133, E226, E241, E242, E704, W503, W504, W505, E741 -max-line-length = 100 +max-line-length = 105 +# + E203 because of a difference of opinion with black +ignore = E121, E123, E126, E133, E226, E241, E242, E704, W503, W504, W505, E203 +