From 87232708256406b629b881793e16e08158526589 Mon Sep 17 00:00:00 2001 From: Anton Dekusar <62334182+adekusar-drl@users.noreply.github.com> Date: Tue, 17 Nov 2020 15:12:14 +0000 Subject: [PATCH] Remove dependency on internal DOCplex methods. (#1441) * docplex corrections * more on docplex methods. * fixes in docplex.py --- .../applications/ising/docplex.py | 45 ++++++++++++++++--- .../problems/quadratic_program.py | 29 +++++++----- 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/qiskit/optimization/applications/ising/docplex.py b/qiskit/optimization/applications/ising/docplex.py index 5b162b7534..e4846f8ec7 100644 --- a/qiskit/optimization/applications/ising/docplex.py +++ b/qiskit/optimization/applications/ising/docplex.py @@ -57,12 +57,14 @@ """ -from typing import Tuple +from typing import Tuple, Union, List import logging from math import fsum import numpy as np from docplex.mp.constants import ComparisonType +from docplex.mp.constr import LinearConstraint, QuadraticConstraint +from docplex.mp.linear import Var from docplex.mp.model import Model from qiskit.quantum_info import Pauli @@ -113,7 +115,7 @@ def get_operator(mdl: Model, auto_penalty: bool = True, # initialize Hamiltonian. num_nodes = len(q_d) pauli_list = [] - shift = 0 + shift = 0. zero = np.zeros(num_nodes, dtype=np.bool) # convert a constant part of the object function into Hamiltonian. @@ -157,13 +159,15 @@ def get_operator(mdl: Model, auto_penalty: bool = True, # convert constraints into penalty terms. for constraint in mdl.iter_constraints(): - constant = constraint.cplex_num_rhs() + right_cst = constraint.get_right_expr().get_constant() + left_cst = constraint.get_left_expr().get_constant() + constant = float(right_cst - left_cst) # constant parts of penalty*(Constant-func)**2: penalty*(Constant**2) shift += penalty * constant ** 2 # linear parts of penalty*(Constant-func)**2: penalty*(-2*Constant*func) - for __l in constraint.iter_net_linear_coefs(): + for __l in _iter_net_linear_coeffs(constraint): z_p = np.zeros(num_nodes, dtype=np.bool) index = q_d[__l[0]] weight = __l[1] @@ -173,8 +177,8 @@ def get_operator(mdl: Model, auto_penalty: bool = True, shift += -penalty * constant * weight # quadratic parts of penalty*(Constant-func)**2: penalty*(func**2) - for __l in constraint.iter_net_linear_coefs(): - for l_2 in constraint.iter_net_linear_coefs(): + for __l in _iter_net_linear_coeffs(constraint): + for l_2 in _iter_net_linear_coeffs(constraint): index1 = q_d[__l[0]] index2 = q_d[l_2[0]] weight1 = __l[1] @@ -205,6 +209,35 @@ def get_operator(mdl: Model, auto_penalty: bool = True, return qubit_op, shift +def _iter_net_linear_coeffs(constraint: Union[LinearConstraint, QuadraticConstraint]) \ + -> List[Tuple[Var, float]]: + """ + Builds a list of tuples, where each tuple contains a decision variable and a + corresponding coefficient of this variable in the constraint. + + Args: + constraint: A constraint to analyze. + + Returns: + A list of tuples of variables and coefficients. + """ + left_expr = constraint.get_left_expr() + right_expr = constraint.get_right_expr() + # for linear constraints we may get an instance of Var instead of expression, + # e.g. x + y = z + if isinstance(left_expr, Var): + left_expr = left_expr + 0 + if isinstance(right_expr, Var): + right_expr = right_expr + 0 + + variables = {} + for var in left_expr.iter_variables(): + variables[var] = left_expr.get_coef(var) + for var in right_expr.iter_variables(): + variables[var] = variables.get(var, 0.0) - right_expr.get_coef(var) + return list(variables.items()) + + def _validate_input_model(mdl: Model) -> None: """Check whether an input model is valid. If not, raise an AquaError. diff --git a/qiskit/optimization/problems/quadratic_program.py b/qiskit/optimization/problems/quadratic_program.py index d756487016..54155b1b39 100644 --- a/qiskit/optimization/problems/quadratic_program.py +++ b/qiskit/optimization/problems/quadratic_program.py @@ -561,11 +561,11 @@ def from_docplex(self, model: Model) -> None: # keep track of names separately, since docplex allows to have None names. var_names = {} for x in model.iter_variables(): - if isinstance(x.get_vartype(), ContinuousVarType): + if isinstance(x.vartype, ContinuousVarType): x_new = self.continuous_var(x.lb, x.ub, x.name) - elif isinstance(x.get_vartype(), BinaryVarType): + elif isinstance(x.vartype, BinaryVarType): x_new = self.binary_var(x.name) - elif isinstance(x.get_vartype(), IntegerVarType): + elif isinstance(x.vartype, IntegerVarType): x_new = self.integer_var(x.lb, x.ub, x.name) else: raise QiskitOptimizationError( @@ -591,7 +591,7 @@ def from_docplex(self, model: Model) -> None: # get quadratic part of objective quadratic = {} if isinstance(model.objective_expr, QuadExpr): - for quad_triplet in model.objective_expr.generate_quad_triplets(): + for quad_triplet in model.objective_expr.iter_quad_triplets(): i = var_names[quad_triplet[0]] j = var_names[quad_triplet[1]] v = quad_triplet[2] @@ -618,15 +618,22 @@ def from_docplex(self, model: Model) -> None: name = constraint.name sense = constraint.sense - rhs = 0 - if not isinstance(constraint.lhs, Var): - rhs -= constraint.lhs.constant - if not isinstance(constraint.rhs, Var): - rhs += constraint.rhs.constant + left_expr = constraint.get_left_expr() + right_expr = constraint.get_right_expr() + # for linear constraints we may get an instance of Var instead of expression, + # e.g. x + y = z + if isinstance(left_expr, Var): + left_expr = left_expr + 0 + if isinstance(right_expr, Var): + right_expr = right_expr + 0 + + rhs = right_expr.constant - left_expr.constant lhs = {} - for x in constraint.iter_net_linear_coefs(): - lhs[var_names[x[0]]] = x[1] + for x in left_expr.iter_variables(): + lhs[var_names[x]] = left_expr.get_coef(x) + for x in right_expr.iter_variables(): + 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)