Skip to content
This repository has been archived by the owner on Dec 7, 2021. It is now read-only.

Remove dependency on internal DOCplex methods. #1441

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 39 additions & 6 deletions qiskit/optimization/applications/ising/docplex.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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]
Expand All @@ -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]
Expand Down Expand Up @@ -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.

Expand Down
29 changes: 18 additions & 11 deletions qiskit/optimization/problems/quadratic_program.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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]
Expand All @@ -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)
Expand Down