qiskit_optimization.translators.gurobipy のソースコード

# This code is part of a Qiskit project.
#
# (C) Copyright IBM 2021, 2023.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Translator between a gurobipy model and a quadratic program"""

from typing import cast

import qiskit_optimization.optionals as _optionals
from qiskit_optimization.exceptions import QiskitOptimizationError
from qiskit_optimization.problems.constraint import Constraint
from qiskit_optimization.problems.quadratic_objective import QuadraticObjective
from qiskit_optimization.problems.quadratic_program import QuadraticProgram
from qiskit_optimization.problems.variable import Variable

if _optionals.HAS_GUROBIPY:
    # pylint: disable=import-error,no-name-in-module
    from gurobipy import LinExpr, Model, QuadExpr
else:

    class Model:  # type: ignore
        """Empty Model class
        Replacement if gurobipy.Model is not present.
        """

        pass


[ドキュメント]@_optionals.HAS_GUROBIPY.require_in_call def to_gurobipy(quadratic_program: QuadraticProgram) -> Model: """Returns a gurobipy model corresponding to a quadratic program. Args: quadratic_program: The quadratic program to be translated. Returns: The gurobipy model corresponding to a quadratic program. Raises: QiskitOptimizationError: if non-supported elements (should never happen). """ # initialize model # pylint: disable=import-error import gurobipy as gp mdl = gp.Model(quadratic_program.name) # add variables var = {} for idx, x in enumerate(quadratic_program.variables): if x.vartype == Variable.Type.CONTINUOUS: var[idx] = mdl.addVar( vtype=gp.GRB.CONTINUOUS, lb=x.lowerbound, ub=x.upperbound, name=x.name ) elif x.vartype == Variable.Type.BINARY: var[idx] = mdl.addVar(vtype=gp.GRB.BINARY, name=x.name) elif x.vartype == Variable.Type.INTEGER: var[idx] = mdl.addVar( vtype=gp.GRB.INTEGER, lb=x.lowerbound, ub=x.upperbound, name=x.name ) else: # should never happen raise QiskitOptimizationError(f"Unsupported variable type: {x.vartype}") # add objective objective = QuadExpr(quadratic_program.objective.constant) for i, v in quadratic_program.objective.linear.to_dict().items(): objective += v * var[cast(int, i)] for (i, j), v in quadratic_program.objective.quadratic.to_dict().items(): objective += v * var[cast(int, i)] * var[cast(int, j)] if quadratic_program.objective.sense == QuadraticObjective.Sense.MINIMIZE: mdl.setObjective(objective, sense=gp.GRB.MINIMIZE) else: mdl.setObjective(objective, sense=gp.GRB.MAXIMIZE) # add linear constraints for i, l_constraint in enumerate(quadratic_program.linear_constraints): name = l_constraint.name rhs = l_constraint.rhs if rhs == 0 and l_constraint.linear.coefficients.nnz == 0: continue linear_expr = LinExpr(0) for j, v in l_constraint.linear.to_dict().items(): linear_expr += v * var[cast(int, j)] sense = l_constraint.sense if sense == Constraint.Sense.EQ: mdl.addConstr(linear_expr == rhs, name=name) elif sense == Constraint.Sense.GE: mdl.addConstr(linear_expr >= rhs, name=name) elif sense == Constraint.Sense.LE: mdl.addConstr(linear_expr <= rhs, name=name) else: # should never happen raise QiskitOptimizationError(f"Unsupported constraint sense: {sense}") # add quadratic constraints for i, q_constraint in enumerate(quadratic_program.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 ): continue quadratic_expr = QuadExpr(0) for j, v in q_constraint.linear.to_dict().items(): quadratic_expr += v * var[cast(int, j)] for (j, k), v in q_constraint.quadratic.to_dict().items(): quadratic_expr += v * var[cast(int, j)] * var[cast(int, k)] sense = q_constraint.sense if sense == Constraint.Sense.EQ: mdl.addConstr(quadratic_expr == rhs, name=name) elif sense == Constraint.Sense.GE: mdl.addConstr(quadratic_expr >= rhs, name=name) elif sense == Constraint.Sense.LE: mdl.addConstr(quadratic_expr <= rhs, name=name) else: # should never happen raise QiskitOptimizationError(f"Unsupported constraint sense: {sense}") mdl.update() return mdl
[ドキュメント]@_optionals.HAS_GUROBIPY.require_in_call 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. """ # pylint: disable=import-error import gurobipy as gp 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()): var1 = var_names[cast(QuadExpr, objective).getVar1(i)] var2 = var_names[cast(QuadExpr, objective).getVar2(i)] coeff = objective.getCoeff(i) quadratic[var1, var2] = coeff # 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 l_constraint in model.getConstrs(): name = l_constraint.ConstrName sense = l_constraint.Sense l_left_expr = model.getRow(l_constraint) rhs = l_constraint.RHS lhs = {} for i in range(l_left_expr.size()): lhs[var_names[l_left_expr.getVar(i)]] = l_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: {l_constraint}") # get quadratic constraints for q_constraint in model.getQConstrs(): name = q_constraint.QCName sense = q_constraint.QCSense q_left_expr = model.getQCRow(q_constraint) rhs = q_constraint.QCRHS linear = {} quadratic = {} linear_part = q_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(q_left_expr.size()): var1 = var_names[q_left_expr.getVar1(i)] var2 = var_names[q_left_expr.getVar2(i)] coeff = q_left_expr.getCoeff(i) quadratic[var1, var2] = coeff 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: {q_constraint}") return quadratic_program