Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
    def _linear_constraint(
            cls, var_names: Dict[Var, str], 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, (AbstractLinearExpr, Var)):
            raise QiskitOptimizationError(
                f"Unsupported expression: {left_expr} {type(left_expr)}")
        if not isinstance(right_expr, (AbstractLinearExpr, Var)):
            raise QiskitOptimizationError(
                f"Unsupported expression: {right_expr} {type(right_expr)}")
        if isinstance(left_expr, Var):
            left_expr = left_expr + 0
        if isinstance(right_expr, Var):
            right_expr = right_expr + 0

        linear = {}
        for x in left_expr.iter_variables():
            linear[var_names[x]] = left_expr.get_coef(x)
        for x in right_expr.iter_variables():
            linear[var_names[x]] = linear.get(var_names[x],
                                              0.0) - right_expr.get_coef(x)

        rhs = right_expr.constant - left_expr.constant

        if constraint.sense not in cls._sense_dict:
            raise QiskitOptimizationError(
                f"Unsupported constraint sense: {constraint}")

        return linear, cls._sense_dict[constraint.sense], rhs
Ejemplo n.º 4
0
    def parse_tsplib_format(filename: str) -> "Tsp":
        """Read a graph in TSPLIB format from file and return a Tsp instance.

        Args:
            filename: the name of the file.

        Raises:
            QiskitOptimizationError: If the type is not "TSP"
            QiskitOptimizationError: If the edge weight type is not "EUC_2D"

        Returns:
            A Tsp instance data.
        """
        name = ""
        coord = []  # type: ignore
        with open(filename) as infile:
            coord_section = False
            for line in infile:
                if line.startswith("NAME"):
                    name = line.split(":")[1]
                    name.strip()
                elif line.startswith("TYPE"):
                    typ = line.split(":")[1]
                    typ.strip()
                    if typ != "TSP":
                        raise QiskitOptimizationError(
                            'This supports only "TSP" type. Actual: {}'.format(typ)
                        )
                elif line.startswith("DIMENSION"):
                    dim = int(line.split(":")[1])
                    coord = np.zeros((dim, 2))  # type: ignore
                elif line.startswith("EDGE_WEIGHT_TYPE"):
                    typ = line.split(":")[1]
                    typ.strip()
                    if typ != "EUC_2D":
                        raise QiskitOptimizationError(
                            'This supports only "EUC_2D" edge weight. Actual: {}'.format(typ)
                        )
                elif line.startswith("NODE_COORD_SECTION"):
                    coord_section = True
                elif coord_section:
                    v = line.split()
                    index = int(v[0]) - 1
                    coord[index][0] = float(v[1])
                    coord[index][1] = float(v[2])

        x_max = max(coord_[0] for coord_ in coord)
        x_min = min(coord_[0] for coord_ in coord)
        y_max = max(coord_[1] for coord_ in coord)
        y_min = min(coord_[1] for coord_ in coord)

        graph = nx.random_geometric_graph(
            len(coord), np.hypot(x_max - x_min, y_max - y_min) + 1, pos=coord
        )
        for w, v in graph.edges:
            delta = [graph.nodes[w]["pos"][i] - graph.nodes[v]["pos"][i] for i in range(2)]
            graph.edges[w, v]["weight"] = np.rint(np.hypot(delta[0], delta[1]))
        return Tsp(graph)
Ejemplo n.º 5
0
    def _quadratic_constraint(
        cls, var_names: Dict[Var, str], 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, (QuadExpr, AbstractLinearExpr, Var)):
            raise QiskitOptimizationError(
                f"Unsupported expression: {left_expr} {type(left_expr)}")
        if not isinstance(right_expr, (QuadExpr, AbstractLinearExpr, Var)):
            raise QiskitOptimizationError(
                f"Unsupported expression: {right_expr} {type(right_expr)}")

        lin = {}
        quad = {}

        if left_expr.is_quad_expr():
            for x in left_expr.linear_part.iter_variables():
                lin[var_names[x]] = left_expr.linear_part.get_coef(x)
            for quad_triplet in left_expr.iter_quad_triplets():
                i = var_names[quad_triplet[0]]
                j = var_names[quad_triplet[1]]
                v = quad_triplet[2]
                quad[i, j] = v
        else:
            for x in left_expr.iter_variables():
                lin[var_names[x]] = left_expr.get_coef(x)

        if right_expr.is_quad_expr():
            for x in right_expr.linear_part.iter_variables():
                lin[var_names[x]] = lin.get(
                    var_names[x], 0.0) - right_expr.linear_part.get_coef(x)
            for quad_triplet in right_expr.iter_quad_triplets():
                i = var_names[quad_triplet[0]]
                j = var_names[quad_triplet[1]]
                v = quad_triplet[2]
                quad[i, j] = quad.get((i, j), 0.0) - v
        else:
            for x in right_expr.iter_variables():
                lin[var_names[x]] = lin.get(var_names[x],
                                            0.0) - right_expr.get_coef(x)

        rhs = right_expr.constant - left_expr.constant

        if constraint.sense not in cls._sense_dict:
            raise QiskitOptimizationError(
                f"Unsupported constraint sense: {constraint}")

        return lin, quad, cls._sense_dict[constraint.sense], rhs
Ejemplo n.º 6
0
    def optimization_level(self, optimization_level: Optional[int] = None):
        """Set the optimization level."""
        if optimization_level is not None and self.use_swap_strategies:
            raise QiskitOptimizationError(
                "optimization_level cannot be set if use_swap_strategies is True."
            )

        self._optimization_level = optimization_level
Ejemplo n.º 7
0
 def _indicator_constraints(
     cls,
     var_names: Dict[Var, str],
     var_bounds: Dict[str, Tuple[float, float]],
     constraint: IndicatorConstraint,
     indicator_big_m: Optional[float] = None,
 ):
     name = constraint.name
     binary_var = constraint.binary_var
     active_value = constraint.active_value
     linear_constraint = constraint.linear_constraint
     linear, sense, rhs = cls._linear_constraint(var_names,
                                                 linear_constraint)
     linear_lb, linear_ub = cls._linear_bounds(var_bounds, linear)
     if sense == "<=":
         big_m = max(0.0, linear_ub -
                     rhs) if indicator_big_m is None else indicator_big_m
         if active_value:
             linear[binary_var.name] = big_m
             rhs += big_m
         else:
             linear[binary_var.name] = -big_m
         return [(linear, sense, rhs, name)]
     elif sense == ">=":
         big_m = max(
             0.0, rhs -
             linear_lb) if indicator_big_m is None else indicator_big_m
         if active_value:
             linear[binary_var.name] = -big_m
             rhs -= big_m
         else:
             linear[binary_var.name] = big_m
         return [(linear, sense, rhs, name)]
     elif sense == "==":
         # for equality constraints, add both GE and LE constraints.
         # linear2, rhs2, and big_m2 are for the GE constraint.
         linear2 = linear.copy()
         rhs2 = rhs
         big_m = max(0.0, linear_ub -
                     rhs) if indicator_big_m is None else indicator_big_m
         big_m2 = max(
             0.0, rhs -
             linear_lb) if indicator_big_m is None else indicator_big_m
         if active_value:
             linear[binary_var.name] = big_m
             rhs += big_m
             linear2[binary_var.name] = -big_m2
             rhs2 -= big_m2
         else:
             linear[binary_var.name] = -big_m
             linear2[binary_var.name] = big_m2
         return [(linear, "<=", rhs, name + "_LE"),
                 (linear2, ">=", rhs2, name + "_GE")]
     else:
         raise QiskitOptimizationError(
             f"Internal error: invalid sense of indicator constraint: {sense}"
         )
Ejemplo n.º 8
0
 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)
Ejemplo n.º 9
0
 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
Ejemplo n.º 10
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 the following features of docplex:

    - linear / quadratic objective function
    - linear / quadratic / indicator constraints
    - binary / integer / continuous variables
    - logical expressions (``logical_not``, ``logical_and``, and ``logical_or``)

    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")

    # 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}")

    return _FromDocplexMp(model).quadratic_program(indicator_big_m)
Ejemplo n.º 11
0
    def __init__(
        self,
        optimizer: Optional[Union[Optimizer, Dict[str, Any]]] = None,
        reps: int = 1,
        initial_state: Optional[QuantumCircuit] = None,
        mixer: Union[QuantumCircuit, OperatorBase] = None,
        initial_point: Optional[np.ndarray] = None,
        alpha: float = 1.0,
        provider: Optional[Provider] = None,
        backend: Optional[Backend] = None,
        shots: int = 1024,
        measurement_error_mitigation: bool = False,
        callback: Optional[Callable[[int, np.ndarray, float, float], None]] = None,
        store_intermediate: bool = False,
        use_swap_strategies: bool = False,
        use_initial_mapping: bool = False,
        use_pulse_efficient: bool = False,
        optimization_level: Optional[int] = None,
    ) -> None:
        """
        Args:
            optimizer: An optimizer or dictionary specifying a classical optimizer.
                If a dictionary, only SPSA and QN-SPSA are supported. The dictionary must contain a
                key ``name`` for the name of the optimizer and may contain additional keys for the
                settings. E.g. ``{'name': 'SPSA', 'maxiter': 100}``.
                Per default, SPSA is used.
            reps: the integer parameter :math:`p` as specified in https://arxiv.org/abs/1411.4028,
                Has a minimum valid value of 1.
            initial_state: An optional initial state to prepend the QAOA circuit with
            mixer: the mixer Hamiltonian to evolve with or a custom quantum circuit. Allows support
                of optimizations in constrained subspaces as per https://arxiv.org/abs/1709.03489
                as well as warm-starting the optimization as introduced
                in http://arxiv.org/abs/2009.10095.
            initial_point: An optional initial point (i.e. initial parameter values)
                for the optimizer. If ``None`` a random vector is chosen in the :class:`VQE`
                class in Qiskit terra using a uniform distribution.
            alpha: The fraction of top measurement samples to be used for the expectation value
                (CVaR expectation). Defaults to 1, i.e. using all samples to construct the
                expectation value. This value must be contained in the interval [0, 1].
            provider: The provider.
            backend: The backend to run the circuits on.
            shots: The number of shots to be used
            measurement_error_mitigation: Whether or not to use measurement error mitigation.
            callback: a callback that can access the intermediate data during the optimization.
                Four parameter values are passed to the callback as follows during each evaluation
                by the optimizer for its current set of parameters as it works towards the minimum.
                These are: the evaluation count, the optimizer parameters for the
                ansatz, the evaluated mean and the evaluated standard deviation.
            store_intermediate: Whether or not to store intermediate values of the optimization
                steps. Per default False.
            use_swap_strategies: A boolean on whether or not to use swap strategies when
                transpiling. If this is False then the standard transpiler with the given
                optimization level will run.
            use_initial_mapping: A boolean flag that, if set to True (the default is False), runs
                a heuristic algorithm to permute the Paulis in the cost operator to better fit the
                coupling map and the swap strategy. This is only needed when the optimization
                problem is sparse and when using swap strategies to transpile.
            use_pulse_efficient: A boolean on whether or not to use a pulse-efficient transpilation.
                If this flag is set to False by default. See https://arxiv.org/abs/2105.01063.
            optimization_level: The transpiler optimization level to run if the swap strategies are
                not used. This value defaults to 1 in the QAOA runtime.

        Raises:
            QiskitOptimizationError: if reps is smaller than 1.
            QiskitOptimizationError: if alpha is not in the interval [0, 1].
            QiskitOptimizationError: if optimization_level is not None and use_swap_strategies
                is True.
        """
        if reps < 1:
            raise QiskitOptimizationError(f"reps must be greater than 0, received {reps}.")

        if alpha < 0 or alpha > 1:
            raise QiskitOptimizationError(f"alpha must range from 0 to 1. Received {alpha}.")

        super().__init__(
            ansatz=None,
            optimizer=optimizer,
            initial_point=initial_point,
            provider=provider,
            backend=backend,
            shots=shots,
            measurement_error_mitigation=measurement_error_mitigation,
            callback=callback,
            store_intermediate=store_intermediate,
        )
        self._initial_state = initial_state
        self._mixer = mixer
        self._reps = reps
        self._use_swap_strategies = use_swap_strategies
        self._use_initial_mapping = use_initial_mapping
        self._use_pulse_efficient = use_pulse_efficient
        self._alpha = alpha
        self._program_id = "qaoa"

        # Use the setter to check consistency with other settings.
        self.optimization_level = optimization_level
Ejemplo n.º 12
0
 def ansatz(self, ansatz: QuantumCircuit) -> None:
     raise QiskitOptimizationError(
         "Cannot set the ansatz for QAOA, it is directly inferred from "
         "the problem Hamiltonian."
     )
Ejemplo n.º 13
0
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).
        MissingOptionalLibraryError: if gurobipy is not installed.
    """

    _check_gurobipy_is_installed("to_gurobipy")

    # initialize model
    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 = 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 = 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 = 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
Ejemplo n.º 14
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
Ejemplo n.º 15
0
def to_docplex_mp(quadratic_program: QuadraticProgram) -> Model:
    """Returns a docplex.mp model corresponding to a quadratic program.

    Args:
        quadratic_program: The quadratic program to be translated.

    Returns:
        The docplex.mp model corresponding to a quadratic program.

    Raises:
        QiskitOptimizationError: if the model contains non-supported elements (should never happen).
    """
    # initialize model
    mdl = Model(quadratic_program.name)

    # add variables
    var = {}
    for idx, x in enumerate(quadratic_program.variables):
        if x.vartype == Variable.Type.CONTINUOUS:
            var[idx] = mdl.continuous_var(lb=x.lowerbound,
                                          ub=x.upperbound,
                                          name=x.name)
        elif x.vartype == Variable.Type.BINARY:
            var[idx] = mdl.binary_var(name=x.name)
        elif x.vartype == Variable.Type.INTEGER:
            var[idx] = mdl.integer_var(lb=x.lowerbound,
                                       ub=x.upperbound,
                                       name=x.name)
        else:
            # should never happen
            raise QiskitOptimizationError(
                f"Internal error: unsupported variable type: {x.vartype}")

    # add objective
    objective = 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.minimize(objective)
    else:
        mdl.maximize(objective)

    # 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 = 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.add_constraint(linear_expr == rhs, ctname=name)
        elif sense == Constraint.Sense.GE:
            mdl.add_constraint(linear_expr >= rhs, ctname=name)
        elif sense == Constraint.Sense.LE:
            mdl.add_constraint(linear_expr <= rhs, ctname=name)
        else:
            # should never happen
            raise QiskitOptimizationError(
                f"Internal error: 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 = 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.add_constraint(quadratic_expr == rhs, ctname=name)
        elif sense == Constraint.Sense.GE:
            mdl.add_constraint(quadratic_expr >= rhs, ctname=name)
        elif sense == Constraint.Sense.LE:
            mdl.add_constraint(quadratic_expr <= rhs, ctname=name)
        else:
            # should never happen
            raise QiskitOptimizationError(
                f"Internal error: unsupported constraint sense: {sense}")

    return mdl
Ejemplo n.º 16
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
Ejemplo n.º 17
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
Ejemplo n.º 18
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