Пример #1
0
    def to_quadratic_problem(self):
        mdl = Model()

        x = {
            i: mdl.binary_var(name='x_{0}'.format(i))
            for i in range(self.g.number_of_nodes())
        }

        for u, v in self.g.edges:
            self.g.edges[u, v].setdefault('weight', 1)

        objective = mdl.sum(self.g.edges[i, j]['weight'] * x[i] * (1 - x[j])
                            for i, j in self.g.edges)

        mdl.maximize(objective)

        # print(mdl.export_as_lp_string())

        qp = QuadraticProgram()
        qp.from_docplex(mdl)
        #print(qp.export_as_lp_string())

        self.qp = qp

        return self.qp
Пример #2
0
 def to_quadratic_program(self):
     num_assets = len(self._mu)
     mdl = AdvModel(name='portfolio')
     x = [mdl.binary_var(name='x_{0}'.format(i)) for i in range(num_assets)]
     quad = mdl.quad_matrix_sum(self._sigma, x)
     linear = np.dot(self._mu, x)
     mdl.minimize(quad + linear)
     mdl.add_constraint(mdl.sum(x[i] for i in range(num_assets)) == self._budget)
     qp = QuadraticProgram()
     qp.from_docplex(mdl)
     return qp
    def to_quadratic_program(self) -> QuadraticProgram:
        """Convert a vehicle routing problem instance into a
        :class:`~qiskit_optimization.problems.QuadraticProgram`

        Returns:
            The :class:`~qiskit_optimization.problems.QuadraticProgram` created
            from the vehicle routing problem instance.
        """
        mdl = Model(name='Vehicle routing')
        n = self._graph.number_of_nodes()
        x = {}
        for i in range(n):
            for j in range(n):
                if i != j:
                    x[(i, j)] = mdl.binary_var(name='x_{0}_{1}'.format(i, j))
        mdl.minimize(
            mdl.sum(self._graph.edges[i, j]['weight'] * x[(i, j)]
                    for i in range(n) for j in range(n) if i != j))
        # Only 1 edge goes out from each node
        for i in range(n):
            if i != self.depot:
                mdl.add_constraint(
                    mdl.sum(x[i, j] for j in range(n) if i != j) == 1)
        # Only 1 edge comes into each node
        for j in range(n):
            if j != self.depot:
                mdl.add_constraint(
                    mdl.sum(x[i, j] for i in range(n) if i != j) == 1)
        # For the depot node
        mdl.add_constraint(
            mdl.sum(x[i, self.depot] for i in range(n)
                    if i != self.depot) == self.num_vehicles)
        mdl.add_constraint(
            mdl.sum(x[self.depot, j] for j in range(n)
                    if j != self.depot) == self.num_vehicles)

        # To eliminate sub-routes
        node_list = [i for i in range(n) if i != self.depot]
        clique_set = []
        for i in range(2, len(node_list) + 1):
            for comb in itertools.combinations(node_list, i):
                clique_set.append(list(comb))
        for clique in clique_set:
            mdl.add_constraint(
                mdl.sum(x[(i, j)] for i in clique
                        for j in clique if i != j) <= len(clique) - 1)
        op = QuadraticProgram()
        op.from_docplex(mdl)
        return op
Пример #4
0
 def __init__(self, model: Model):
     """
     Args:
         model: Docplex model
     """
     self._model: Model = model
     self._quadratic_program: QuadraticProgram = QuadraticProgram()
     self._var_names: Dict[Var, str] = {}
     self._var_bounds: Dict[str, Tuple[float, float]] = {}
Пример #5
0
    def to_quadratic_program(self) -> QuadraticProgram:
        """Convert a vertex cover instance into a
        :class:`~qiskit_optimization.problems.QuadraticProgram`

        Returns:
            The :class:`~qiskit_optimization.problems.QuadraticProgram` created
            from the vertex cover instance.
        """
        mdl = Model(name='Vertex cover')
        n = self._graph.number_of_nodes()
        x = {i: mdl.binary_var(name='x_{0}'.format(i)) for i in range(n)}
        objective = mdl.sum(x[i] for i in x)
        for w, v in self._graph.edges:
            mdl.add_constraint(x[w] + x[v] >= 1)
        mdl.minimize(objective)
        op = QuadraticProgram()
        op.from_docplex(mdl)
        return op
Пример #6
0
    def to_quadratic_program(self) -> QuadraticProgram:
        """Convert a number partitioning problem instance into a
        :class:`~qiskit_optimization.problems.QuadraticProgram`

        Returns:
            The :class:`~qiskit_optimization.problems.QuadraticProgram` created
            from the number partitioning problem instance.
        """
        mdl = Model(name='Number partitioning')
        x = {
            i: mdl.binary_var(name='x_{0}'.format(i))
            for i in range(len(self._number_set))
        }
        mdl.add_constraint(
            mdl.sum(num * (-2 * x[i] + 1)
                    for i, num in enumerate(self._number_set)) == 0)
        op = QuadraticProgram()
        op.from_docplex(mdl)
        return op
Пример #7
0
    def to_quadratic_program(self) -> QuadraticProgram:
        """Convert a knapsack problem instance into a
        :class:`~qiskit_optimization.problems.QuadraticProgram`

        Returns:
            The :class:`~qiskit_optimization.problems.QuadraticProgram` created
            from the knapsack problem instance.
        """
        mdl = Model(name="Knapsack")
        x = {
            i: mdl.binary_var(name="x_{0}".format(i))
            for i in range(len(self._values))
        }
        mdl.maximize(mdl.sum(self._values[i] * x[i] for i in x))
        mdl.add_constraint(
            mdl.sum(self._weights[i] * x[i] for i in x) <= self._max_weight)
        op = QuadraticProgram()
        op.from_docplex(mdl)
        return op
Пример #8
0
    def to_quadratic_program(self) -> QuadraticProgram:
        """Convert a stable set instance into a
        :class:`~qiskit_optimization.problems.QuadraticProgram`

        Returns:
            The :class:`~qiskit_optimization.problems.QuadraticProgram` created
            from the stable set instance.
        """
        mdl = Model(name='Stable set')
        n = self._graph.number_of_nodes()
        x = {i: mdl.binary_var(name='x_{0}'.format(i)) for i in range(n)}
        for w, v in self._graph.edges:
            self._graph.edges[w, v].setdefault('weight', 1)
        objective = mdl.sum(x[i] for i in x)
        for w, v in self._graph.edges:
            mdl.add_constraint(x[w] + x[v] <= 1)
        mdl.maximize(objective)
        op = QuadraticProgram()
        op.from_docplex(mdl)
        return op
Пример #9
0
    def to_quadratic_program(self) -> QuadraticProgram:
        """Convert a Max-cut problem instance into a
        :class:`~qiskit_optimization.problems.QuadraticProgram`

        Returns:
            The :class:`~qiskit_optimization.problems.QuadraticProgram` created
            from the Max-cut problem instance.
        """
        mdl = Model(name='Max-cut')
        x = {i: mdl.binary_var(name='x_{0}'.format(i))
             for i in range(self._graph.number_of_nodes())}
        for w, v in self._graph.edges:
            self._graph.edges[w, v].setdefault('weight', 1)
        objective = mdl.sum(self._graph.edges[i, j]['weight'] * x[i]
                            * (1 - x[j]) + self._graph.edges[i, j]['weight'] * x[j]
                            * (1 - x[i]) for i, j in self._graph.edges)
        mdl.maximize(objective)
        op = QuadraticProgram()
        op.from_docplex(mdl)
        return op
Пример #10
0
    def to_quadratic_program(self) -> QuadraticProgram:
        """Convert a graph partition instance into a
        :class:`~qiskit_optimization.problems.QuadraticProgram`

        Returns:
            The :class:`~qiskit_optimization.problems.QuadraticProgram` created
            from the graph partition instance.
        """
        mdl = Model(name='Graph partition')
        n = self._graph.number_of_nodes()
        x = {i: mdl.binary_var(name='x_{0}'.format(i)) for i in range(n)}
        for w, v in self._graph.edges:
            self._graph.edges[w, v].setdefault('weight', 1)
        objective = mdl.sum(self._graph.edges[i, j]['weight'] *
                            (x[i] + x[j] - 2*x[i]*x[j]) for i, j in self._graph.edges)
        mdl.minimize(objective)
        mdl.add_constraint(mdl.sum([x[i] for i in x]) == n//2)
        op = QuadraticProgram()
        op.from_docplex(mdl)
        return op
Пример #11
0
    def to_quadratic_program(self) -> QuadraticProgram:
        """Convert a set packing instance into a
        :class:`~qiskit_optimization.problems.QuadraticProgram`

        Returns:
            The :class:`~qiskit_optimization.problems.QuadraticProgram` created
            from the set packing instance.
        """
        mdl = Model(name='Set packing')
        x = {
            i: mdl.binary_var(name='x_{0}'.format(i))
            for i in range(len(self._subsets))
        }
        mdl.maximize(mdl.sum(x[i] for i in x))
        for element in self._set:
            mdl.add_constraint(
                mdl.sum(x[i] for i, sub in enumerate(self._subsets)
                        if element in sub) <= 1)
        op = QuadraticProgram()
        op.from_docplex(mdl)
        return op
Пример #12
0
    def to_quadratic_program(self) -> QuadraticProgram:
        """Convert a clique problem instance into a
        :class:`~qiskit_optimization.problems.QuadraticProgram`.
        When "size" is None, this makes an optimization model for a maximal clique
        instead of the specified size of a clique.

        Returns:
            The :class:`~qiskit_optimization.problems.QuadraticProgram` created
            from the clique problem instance.
        """
        complement_g = nx.complement(self._graph)

        mdl = Model(name='Clique')
        n = self._graph.number_of_nodes()
        x = {i: mdl.binary_var(name='x_{0}'.format(i)) for i in range(n)}
        for w, v in complement_g.edges:
            mdl.add_constraint(x[w] + x[v] <= 1)
        if self.size is None:
            mdl.maximize(mdl.sum(x[i] for i in x))
        else:
            mdl.add_constraint(mdl.sum(x[i] for i in x) == self.size)
        op = QuadraticProgram()
        op.from_docplex(mdl)
        return op
Пример #13
0
def to_ising(quad_prog: QuadraticProgram) -> Tuple[OperatorBase, float]:
    """Return the Ising Hamiltonian of this problem.

    Variables are mapped to qubits in the same order, i.e.,
    i-th variable is mapped to i-th qubit.
    See https://github.com/Qiskit/qiskit-terra/issues/1148 for details.

    Returns:
        qubit_op: The qubit operator for the problem
        offset: The constant value in the Ising Hamiltonian.

    Raises:
        QiskitOptimizationError: If an integer variable or a continuous variable exists
            in the problem.
        QiskitOptimizationError: If constraints exist in the problem.
    """
    # if problem has variables that are not binary, raise an error
    if quad_prog.get_num_vars() > quad_prog.get_num_binary_vars():
        raise QiskitOptimizationError(
            "The type of all variables must be binary. "
            "You can use `QuadraticProgramToQubo` converter "
            "to convert integer variables to binary variables. "
            "If the problem contains continuous variables, `to_ising` cannot handle it. "
            "You might be able to solve it with `ADMMOptimizer`.")

    # if constraints exist, raise an error
    if quad_prog.linear_constraints or quad_prog.quadratic_constraints:
        raise QiskitOptimizationError(
            "There must be no constraint in the problem. "
            "You can use `QuadraticProgramToQubo` converter "
            "to convert constraints to penalty terms of the objective function."
        )

    # initialize Hamiltonian.
    num_nodes = quad_prog.get_num_vars()
    pauli_list = []
    offset = 0.0
    zero = np.zeros(num_nodes, dtype=bool)

    # set a sign corresponding to a maximized or minimized problem.
    # sign == 1 is for minimized problem. sign == -1 is for maximized problem.
    sense = quad_prog.objective.sense.value

    # convert a constant part of the object function into Hamiltonian.
    offset += quad_prog.objective.constant * sense

    # convert linear parts of the object function into Hamiltonian.
    for idx, coef in quad_prog.objective.linear.to_dict().items():
        z_p = zero.copy()
        weight = coef * sense / 2
        z_p[idx] = True

        pauli_list.append(PauliOp(Pauli((z_p, zero)), -weight))
        offset += weight

    # create Pauli terms
    for (i, j), coeff in quad_prog.objective.quadratic.to_dict().items():
        weight = coeff * sense / 4

        if i == j:
            offset += weight
        else:
            z_p = zero.copy()
            z_p[i] = True
            z_p[j] = True
            pauli_list.append(PauliOp(Pauli((z_p, zero)), weight))

        z_p = zero.copy()
        z_p[i] = True
        pauli_list.append(PauliOp(Pauli((z_p, zero)), -weight))

        z_p = zero.copy()
        z_p[j] = True
        pauli_list.append(PauliOp(Pauli((z_p, zero)), -weight))

        offset += weight

    # Remove paulis whose coefficients are zeros.
    qubit_op = sum(pauli_list)

    # qubit_op could be the integer 0, in this case return an identity operator of
    # appropriate size
    if isinstance(qubit_op, OperatorBase):
        qubit_op = qubit_op.reduce()
    else:
        qubit_op = I ^ num_nodes

    return qubit_op, offset
Пример #14
0
class _FromDocplexMp:
    _sense_dict = {
        ComparisonType.EQ: "==",
        ComparisonType.LE: "<=",
        ComparisonType.GE: ">="
    }

    def __init__(self, model: Model):
        """
        Args:
            model: Docplex model
        """
        self._model: Model = model
        self._quadratic_program: QuadraticProgram = QuadraticProgram()
        self._var_names: Dict[Var, str] = {}
        self._var_bounds: Dict[str, Tuple[float, float]] = {}

    def _variables(self):
        # keep track of names separately, since docplex allows to have None names.
        for x in self._model.iter_variables():
            if isinstance(x.vartype, ContinuousVarType):
                x_new = self._quadratic_program.continuous_var(
                    x.lb, x.ub, x.name)
            elif isinstance(x.vartype, BinaryVarType):
                x_new = self._quadratic_program.binary_var(x.name)
            elif isinstance(x.vartype, IntegerVarType):
                x_new = self._quadratic_program.integer_var(x.lb, x.ub, x.name)
            else:
                raise QiskitOptimizationError(
                    f"Unsupported variable type: {x.name} {x.vartype}")
            self._var_names[x] = x_new.name
            self._var_bounds[x.name] = (x_new.lowerbound, x_new.upperbound)

    def _linear_expr(self, expr: AbstractLinearExpr) -> Dict[str, float]:
        # AbstractLinearExpr is a parent of LinearExpr, ConstantExpr, and ZeroExpr
        linear = {}
        for x, coeff in expr.iter_terms():
            linear[self._var_names[x]] = coeff
        return linear

    def _quadratic_expr(
        self, expr: QuadExpr
    ) -> Tuple[Dict[str, float], Dict[Tuple[str, str], float]]:
        linear = self._linear_expr(expr.get_linear_part())
        quad = {}
        for x, y, coeff in expr.iter_quad_triplets():
            i = self._var_names[x]
            j = self._var_names[y]
            quad[i, j] = coeff
        return linear, quad

    def quadratic_program(
            self, indicator_big_m: Optional[float]) -> QuadraticProgram:
        """Generate a quadratic program corresponding to the input Docplex model.

        Args:
            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:
            a quadratic program corresponding to the input Docplex model.

        """
        self._quadratic_program = QuadraticProgram(self._model.name)

        # prepare variables
        self._variables()

        # objective sense
        minimize = self._model.objective_sense.is_minimize()

        # make sure objective expression is linear or quadratic and not a variable
        if isinstance(self._model.objective_expr, Var):
            self._model.objective_expr = self._model.objective_expr + 0  # Var + 0 -> LinearExpr

        constant = self._model.objective_expr.constant
        if isinstance(self._model.objective_expr, QuadExpr):
            linear, quadratic = self._quadratic_expr(
                self._model.objective_expr)
        else:
            linear = self._linear_expr(
                self._model.objective_expr.get_linear_part())
            quadratic = {}

        # set objective
        if minimize:
            self._quadratic_program.minimize(constant, linear, quadratic)
        else:
            self._quadratic_program.maximize(constant, linear, quadratic)

        # set linear constraints
        for constraint in self._model.iter_linear_constraints():
            linear, sense, rhs = self._linear_constraint(constraint)
            if not linear:  # lhs == 0
                warn(f"Trivial constraint: {constraint}", stacklevel=3)
            self._quadratic_program.linear_constraint(linear, sense, rhs,
                                                      constraint.name)

        # set quadratic constraints
        for constraint in self._model.iter_quadratic_constraints():
            linear, quadratic, sense, rhs = self._quadratic_constraint(
                constraint)
            if not linear and not quadratic:  # lhs == 0
                warn(f"Trivial constraint: {constraint}", stacklevel=3)
            self._quadratic_program.quadratic_constraint(
                linear, quadratic, sense, rhs, constraint.name)

        # set indicator constraints
        for index, constraint in enumerate(
                self._model.iter_indicator_constraints()):
            linear, _, _ = self._linear_constraint(
                constraint.linear_constraint)
            if not linear:  # lhs == 0
                warn(f"Trivial constraint: {constraint}", stacklevel=3)
            prefix = constraint.name or f"ind{index}"
            linear_constraints = self._indicator_constraints(
                constraint, prefix, indicator_big_m)
            for linear, sense, rhs, name in linear_constraints:
                self._quadratic_program.linear_constraint(
                    linear, sense, rhs, name)

        return self._quadratic_program

    @staticmethod
    def _subtract(dict1: Dict[Any, float],
                  dict2: Dict[Any, float]) -> Dict[Any, float]:
        """Calculate dict1 - dict2"""
        ret = dict1.copy()
        for key, val2 in dict2.items():
            if key in dict1:
                val1 = ret[key]
                if isclose(val1, val2):
                    del ret[key]
                else:
                    ret[key] -= val2
            else:
                ret[key] = -val2
        return ret

    def _linear_constraint(
            self, constraint: LinearConstraint
    ) -> Tuple[Dict[str, float], str, float]:
        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 not isinstance(left_expr, (Expr, Var)):
            raise QiskitOptimizationError(
                f"Unsupported expression: {left_expr} {type(left_expr)}")
        if not isinstance(right_expr, (Expr, Var)):
            raise QiskitOptimizationError(
                f"Unsupported expression: {right_expr} {type(right_expr)}")
        if constraint.sense not in self._sense_dict:
            raise QiskitOptimizationError(
                f"Unsupported constraint sense: {constraint}")

        if isinstance(left_expr, Var):
            left_expr = left_expr + 0  # Var + 0 -> LinearExpr
        left_linear = self._linear_expr(left_expr)

        if isinstance(right_expr, Var):
            right_expr = right_expr + 0
        right_linear = self._linear_expr(right_expr)

        linear = self._subtract(left_linear, right_linear)
        rhs = right_expr.constant - left_expr.constant
        return linear, self._sense_dict[constraint.sense], rhs

    def _quadratic_constraint(
        self, constraint: QuadraticConstraint
    ) -> Tuple[Dict[str, float], Dict[Tuple[str, str], float], str, float]:
        left_expr = constraint.get_left_expr()
        right_expr = constraint.get_right_expr()
        if not isinstance(left_expr, (Expr, Var)):
            raise QiskitOptimizationError(
                f"Unsupported expression: {left_expr} {type(left_expr)}")
        if not isinstance(right_expr, (Expr, Var)):
            raise QiskitOptimizationError(
                f"Unsupported expression: {right_expr} {type(right_expr)}")
        if constraint.sense not in self._sense_dict:
            raise QiskitOptimizationError(
                f"Unsupported constraint sense: {constraint}")

        if isinstance(left_expr, Var):
            left_expr = left_expr + 0  # Var + 0 -> LinearExpr
        if left_expr.is_quad_expr():
            left_lin, left_quad = self._quadratic_expr(left_expr)
        else:
            left_lin = self._linear_expr(left_expr)
            left_quad = {}

        if isinstance(right_expr, Var):
            right_expr = right_expr + 0
        if right_expr.is_quad_expr():
            right_lin, right_quad = self._quadratic_expr(right_expr)
        else:
            right_lin = self._linear_expr(right_expr)
            right_quad = {}

        linear = self._subtract(left_lin, right_lin)
        quadratic = self._subtract(left_quad, right_quad)
        rhs = right_expr.constant - left_expr.constant
        return linear, quadratic, self._sense_dict[constraint.sense], rhs

    def _linear_bounds(self, linear: Dict[str, float]):
        linear_lb = 0.0
        linear_ub = 0.0
        for var_name, val in linear.items():
            x_lb, x_ub = self._var_bounds[var_name]
            x_lb *= val
            x_ub *= val
            linear_lb += min(x_lb, x_ub)
            linear_ub += max(x_lb, x_ub)
        return linear_lb, linear_ub

    def _indicator_constraints(
        self,
        constraint: IndicatorConstraint,
        name: str,
        indicator_big_m: Optional[float] = None,
    ):
        binary_var = constraint.binary_var
        active_value = constraint.active_value
        linear_constraint = constraint.linear_constraint
        linear, sense, rhs = self._linear_constraint(linear_constraint)
        linear_lb, linear_ub = self._linear_bounds(linear)
        ret = []
        if sense in ["<=", "=="]:
            big_m = max(0.0, linear_ub -
                        rhs) if indicator_big_m is None else indicator_big_m
            if active_value:
                # rhs += big_m * (1 - binary_var)
                linear2 = self._subtract(linear, {binary_var.name: -big_m})
                rhs2 = rhs + big_m
            else:
                # rhs += big_m * binary_var
                linear2 = self._subtract(linear, {binary_var.name: big_m})
                rhs2 = rhs
            name2 = name + "_LE" if sense == "==" else name
            ret.append((linear2, "<=", rhs2, name2))
        if sense in [">=", "=="]:
            big_m = max(
                0.0, rhs -
                linear_lb) if indicator_big_m is None else indicator_big_m
            if active_value:
                # rhs += -big_m * (1 - binary_var)
                linear2 = self._subtract(linear, {binary_var.name: big_m})
                rhs2 = rhs - big_m
            else:
                # rhs += -big_m * binary_var
                linear2 = self._subtract(linear, {binary_var.name: -big_m})
                rhs2 = rhs
            name2 = name + "_GE" if sense == "==" else name
            ret.append((linear2, ">=", rhs2, name2))
        if sense not in ["<=", ">=", "=="]:
            raise QiskitOptimizationError(
                f"Internal error: invalid sense of indicator constraint: {sense}"
            )
        return ret
Пример #15
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
Пример #16
0
    def quadratic_program(
            self, indicator_big_m: Optional[float]) -> QuadraticProgram:
        """Generate a quadratic program corresponding to the input Docplex model.

        Args:
            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:
            a quadratic program corresponding to the input Docplex model.

        """
        self._quadratic_program = QuadraticProgram(self._model.name)

        # prepare variables
        self._variables()

        # objective sense
        minimize = self._model.objective_sense.is_minimize()

        # make sure objective expression is linear or quadratic and not a variable
        if isinstance(self._model.objective_expr, Var):
            self._model.objective_expr = self._model.objective_expr + 0  # Var + 0 -> LinearExpr

        constant = self._model.objective_expr.constant
        if isinstance(self._model.objective_expr, QuadExpr):
            linear, quadratic = self._quadratic_expr(
                self._model.objective_expr)
        else:
            linear = self._linear_expr(
                self._model.objective_expr.get_linear_part())
            quadratic = {}

        # set objective
        if minimize:
            self._quadratic_program.minimize(constant, linear, quadratic)
        else:
            self._quadratic_program.maximize(constant, linear, quadratic)

        # set linear constraints
        for constraint in self._model.iter_linear_constraints():
            linear, sense, rhs = self._linear_constraint(constraint)
            if not linear:  # lhs == 0
                warn(f"Trivial constraint: {constraint}", stacklevel=3)
            self._quadratic_program.linear_constraint(linear, sense, rhs,
                                                      constraint.name)

        # set quadratic constraints
        for constraint in self._model.iter_quadratic_constraints():
            linear, quadratic, sense, rhs = self._quadratic_constraint(
                constraint)
            if not linear and not quadratic:  # lhs == 0
                warn(f"Trivial constraint: {constraint}", stacklevel=3)
            self._quadratic_program.quadratic_constraint(
                linear, quadratic, sense, rhs, constraint.name)

        # set indicator constraints
        for index, constraint in enumerate(
                self._model.iter_indicator_constraints()):
            linear, _, _ = self._linear_constraint(
                constraint.linear_constraint)
            if not linear:  # lhs == 0
                warn(f"Trivial constraint: {constraint}", stacklevel=3)
            prefix = constraint.name or f"ind{index}"
            linear_constraints = self._indicator_constraints(
                constraint, prefix, indicator_big_m)
            for linear, sense, rhs, name in linear_constraints:
                self._quadratic_program.linear_constraint(
                    linear, sense, rhs, name)

        return self._quadratic_program
Пример #17
0
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
Пример #18
0
def from_ising(
    qubit_op: OperatorBase,
    offset: float = 0.0,
    linear: bool = False,
) -> QuadraticProgram:
    r"""Create a quadratic program from a qubit operator and a shift value.

    Variables are mapped to qubits in the same order, i.e.,
    i-th variable is mapped to i-th qubit.
    See https://github.com/Qiskit/qiskit-terra/issues/1148 for details.

    Args:
        qubit_op: The qubit operator of the problem.
        offset: The constant term in the Ising Hamiltonian.
        linear: If linear is True, :math:`x^2` is treated as a linear term
            since :math:`x^2 = x` for :math:`x \in \{0,1\}`.
            Otherwise, :math:`x^2` is treat as a quadratic term.
            The default value is False.

    Returns:
        The quadratic program corresponding to the qubit operator.

    Raises:
        QiskitOptimizationError: if there are Pauli Xs or Ys in any Pauli term
        QiskitOptimizationError: if there are more than 2 Pauli Zs in any Pauli term
        QiskitOptimizationError: if any Pauli term has an imaginary coefficient
        NotImplementedError: If the input operator is a ListOp
    """
    if isinstance(qubit_op, PauliSumOp):
        qubit_op = qubit_op.to_pauli_op()

    # No support for ListOp yet, this can be added in future
    # pylint: disable=unidiomatic-typecheck
    if type(qubit_op) == ListOp:
        raise NotImplementedError(
            "Conversion of a ListOp is not supported, convert each "
            "operator in the ListOp separately.")

    quad_prog = QuadraticProgram()
    quad_prog.binary_var_list(qubit_op.num_qubits)

    if not isinstance(qubit_op, SummedOp):
        pauli_list = [qubit_op.to_pauli_op()]
    else:
        pauli_list = qubit_op.to_pauli_op()

    # prepare a matrix of coefficients of Pauli terms
    # `pauli_coeffs_diag` is the diagonal part
    # `pauli_coeffs_triu` is the upper triangular part
    pauli_coeffs_diag = [0.0] * qubit_op.num_qubits
    pauli_coeffs_triu = {}

    for pauli_op in pauli_list:
        pauli_op = pauli_op.to_pauli_op()
        pauli = pauli_op.primitive
        coeff = pauli_op.coeff

        if not math.isclose(coeff.imag, 0.0, abs_tol=1e-10):
            raise QiskitOptimizationError(
                f"Imaginary coefficient exists: {pauli_op}")

        if np.any(pauli.x):
            raise QiskitOptimizationError(
                f"Pauli X or Y exists in the Pauli term: {pauli}")

        # indices of Pauli Zs in the Pauli term
        z_index = np.where(pauli.z)[0]
        num_z = len(z_index)

        if num_z == 1:
            pauli_coeffs_diag[z_index[0]] = coeff.real
        elif num_z == 2:
            pauli_coeffs_triu[z_index[0], z_index[1]] = coeff.real
        else:
            raise QiskitOptimizationError(
                f"There are more than 2 Pauli Zs in the Pauli term: {pauli}")

    linear_terms = {}
    quadratic_terms = {}

    # For quadratic pauli terms of operator
    # x_i * x_j = (1 - Z_i - Z_j + Z_i * Z_j)/4
    for (i, j), weight in pauli_coeffs_triu.items():
        # Add a quadratic term to the object function of `QuadraticProgram`
        # The coefficient of the quadratic term in `QuadraticProgram` is
        # 4 * weight of the pauli
        quadratic_terms[i, j] = 4 * weight
        pauli_coeffs_diag[i] += weight
        pauli_coeffs_diag[j] += weight
        offset -= weight

    # After processing quadratic pauli terms, only linear paulis are left
    # x_i = (1 - Z_i)/2
    for i, weight in enumerate(pauli_coeffs_diag):
        # Add a linear term to the object function of `QuadraticProgram`
        # The coefficient of the linear term in `QuadraticProgram` is
        # 2 * weight of the pauli
        if linear:
            linear_terms[i] = -2 * weight
        else:
            quadratic_terms[i, i] = -2 * weight
        offset += weight

    quad_prog.minimize(constant=offset,
                       linear=linear_terms,
                       quadratic=quadratic_terms)

    return quad_prog