def from_gurobipy(model: Model) -> QuadraticProgram: """Translate a gurobipy model into a quadratic program. Note that this supports only basic functions of gurobipy as follows: - quadratic objective function - linear / quadratic constraints - binary / integer / continuous variables Args: model: The gurobipy model to be loaded. Returns: The quadratic program corresponding to the model. Raises: QiskitOptimizationError: if the model contains unsupported elements. MissingOptionalLibraryError: if gurobipy is not installed. """ _check_gurobipy_is_installed("from_gurobipy") if not isinstance(model, Model): raise QiskitOptimizationError(f"The model is not compatible: {model}") quadratic_program = QuadraticProgram() # Update the model to make sure everything works as expected model.update() # get name quadratic_program.name = model.ModelName # get variables # keep track of names separately, since gurobipy allows to have None names. var_names = {} for x in model.getVars(): if x.vtype == gp.GRB.CONTINUOUS: x_new = quadratic_program.continuous_var(x.lb, x.ub, x.VarName) elif x.vtype == gp.GRB.BINARY: x_new = quadratic_program.binary_var(x.VarName) elif x.vtype == gp.GRB.INTEGER: x_new = quadratic_program.integer_var(x.lb, x.ub, x.VarName) else: raise QiskitOptimizationError( f"Unsupported variable type: {x.VarName} {x.vtype}") var_names[x] = x_new.name # objective sense minimize = model.ModelSense == gp.GRB.MINIMIZE # Retrieve the objective objective = model.getObjective() has_quadratic_objective = False # Retrieve the linear part in case it is a quadratic objective if isinstance(objective, gp.QuadExpr): linear_part = objective.getLinExpr() has_quadratic_objective = True else: linear_part = objective # Get the constant constant = linear_part.getConstant() # get linear part of objective linear = {} for i in range(linear_part.size()): linear[var_names[linear_part.getVar(i)]] = linear_part.getCoeff(i) # get quadratic part of objective quadratic = {} if has_quadratic_objective: for i in range(objective.size()): x = var_names[objective.getVar1(i)] y = var_names[objective.getVar2(i)] v = objective.getCoeff(i) quadratic[x, y] = v # set objective if minimize: quadratic_program.minimize(constant, linear, quadratic) else: quadratic_program.maximize(constant, linear, quadratic) # check whether there are any general constraints if model.NumSOS > 0 or model.NumGenConstrs > 0: raise QiskitOptimizationError( "Unsupported constraint: SOS or General Constraint") # get linear constraints for constraint in model.getConstrs(): name = constraint.ConstrName sense = constraint.Sense left_expr = model.getRow(constraint) rhs = constraint.RHS lhs = {} for i in range(left_expr.size()): lhs[var_names[left_expr.getVar(i)]] = left_expr.getCoeff(i) if sense == gp.GRB.EQUAL: quadratic_program.linear_constraint(lhs, "==", rhs, name) elif sense == gp.GRB.GREATER_EQUAL: quadratic_program.linear_constraint(lhs, ">=", rhs, name) elif sense == gp.GRB.LESS_EQUAL: quadratic_program.linear_constraint(lhs, "<=", rhs, name) else: raise QiskitOptimizationError( f"Unsupported constraint sense: {constraint}") # get quadratic constraints for constraint in model.getQConstrs(): name = constraint.QCName sense = constraint.QCSense left_expr = model.getQCRow(constraint) rhs = constraint.QCRHS linear = {} quadratic = {} linear_part = left_expr.getLinExpr() for i in range(linear_part.size()): linear[var_names[linear_part.getVar(i)]] = linear_part.getCoeff(i) for i in range(left_expr.size()): x = var_names[left_expr.getVar1(i)] y = var_names[left_expr.getVar2(i)] v = left_expr.getCoeff(i) quadratic[x, y] = v if sense == gp.GRB.EQUAL: quadratic_program.quadratic_constraint(linear, quadratic, "==", rhs, name) elif sense == gp.GRB.GREATER_EQUAL: quadratic_program.quadratic_constraint(linear, quadratic, ">=", rhs, name) elif sense == gp.GRB.LESS_EQUAL: quadratic_program.quadratic_constraint(linear, quadratic, "<=", rhs, name) else: raise QiskitOptimizationError( f"Unsupported constraint sense: {constraint}") return quadratic_program
def from_docplex_mp( model: Model, indicator_big_m: Optional[float] = None) -> QuadraticProgram: """Translate a docplex.mp model into a quadratic program. Note that this supports only basic functions of docplex as follows: - quadratic objective function - linear / quadratic / indicator constraints - binary / integer / continuous variables Args: model: The docplex.mp model to be loaded. indicator_big_m: The big-M value used for the big-M formulation to convert indicator constraints into linear constraints. If ``None``, it is automatically derived from the model. Returns: The quadratic program corresponding to the model. Raises: QiskitOptimizationError: if the model contains unsupported elements. """ if not isinstance(model, Model): raise QiskitOptimizationError(f"The model is not compatible: {model}") if model.number_of_user_cut_constraints > 0: raise QiskitOptimizationError("User cut constraints are not supported") if model.number_of_lazy_constraints > 0: raise QiskitOptimizationError("Lazy constraints are not supported") if model.number_of_sos > 0: raise QiskitOptimizationError("SOS sets are not supported") # get name quadratic_program = QuadraticProgram(model.name) # get variables # keep track of names separately, since docplex allows to have None names. var_names = {} var_bounds = {} for x in model.iter_variables(): if isinstance(x.vartype, ContinuousVarType): x_new = quadratic_program.continuous_var(x.lb, x.ub, x.name) elif isinstance(x.vartype, BinaryVarType): x_new = quadratic_program.binary_var(x.name) elif isinstance(x.vartype, IntegerVarType): x_new = quadratic_program.integer_var(x.lb, x.ub, x.name) else: raise QiskitOptimizationError( f"Unsupported variable type: {x.name} {x.vartype}") var_names[x] = x_new.name var_bounds[x.name] = (x_new.lowerbound, x_new.upperbound) # objective sense minimize = model.objective_sense.is_minimize() # make sure objective expression is linear or quadratic and not a variable if isinstance(model.objective_expr, Var): model.objective_expr = model.objective_expr + 0 # get objective offset constant = model.objective_expr.constant # get linear part of objective linear = {} linear_part = model.objective_expr.get_linear_part() for x in linear_part.iter_variables(): linear[var_names[x]] = linear_part.get_coef(x) # get quadratic part of objective quadratic = {} if isinstance(model.objective_expr, QuadExpr): 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] quadratic[i, j] = v # set objective if minimize: quadratic_program.minimize(constant, linear, quadratic) else: quadratic_program.maximize(constant, linear, quadratic) # check constraint type for constraint in model.iter_constraints(): # If any constraint is not linear/quadratic/indicator constraints, it raises an error. if isinstance(constraint, LinearConstraint): if isinstance(constraint, NotEqualConstraint): # Notice that NotEqualConstraint is a subclass of Docplex's LinearConstraint, # but it cannot be handled by optimization. raise QiskitOptimizationError( f"Unsupported constraint: {constraint}") elif not isinstance(constraint, (QuadraticConstraint, IndicatorConstraint)): raise QiskitOptimizationError( f"Unsupported constraint: {constraint}") # get linear constraints for constraint in model.iter_linear_constraints(): lhs, sense, rhs = _FromDocplexMp._linear_constraint( var_names, constraint) quadratic_program.linear_constraint(lhs, sense, rhs, constraint.name) # get quadratic constraints for constraint in model.iter_quadratic_constraints(): linear, quadratic, sense, rhs = _FromDocplexMp._quadratic_constraint( var_names, constraint) quadratic_program.quadratic_constraint(linear, quadratic, sense, rhs, constraint.name) # get indicator constraints for constraint in model.iter_indicator_constraints(): linear_constraints = _FromDocplexMp._indicator_constraints( var_names, var_bounds, constraint, indicator_big_m) for linear, sense, rhs, name in linear_constraints: quadratic_program.linear_constraint(linear, sense, rhs, name) return quadratic_program