Beispiel #1
0
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