Exemplo n.º 1
0
def make_sig_lagrangian(f, gts, eqs, p, q):
    """
    Given a problem

    .. math::

        \\begin{align*}
          \min\{ f(x) :~& g(x) \geq 0 \\text{ for } g \\in \\mathtt{gts}, \\\\
                       & g(x) = 0  \\text{ for } g \\in \\mathtt{eqs}, \\\\
                       & \\text{and } x \\in X \}
        \\end{align*}

    construct the q-fold constraints ``q-gts`` and ``q-eqs``, by taking all products
    of ``<= q`` elements from ``gts`` and ``eqs`` respectively. Then form the Lagrangian

    .. math::

        L = f - \\gamma
            - \sum_{g \, \\in  \, \\mathtt{q-gts}} s_g \cdot g
            - \sum_{g \, \\in  \, \\mathtt{q-eqs}} z_g \cdot g

    where :math:`\\gamma` is a coniclifts Variable of dimension 1, and the coefficients
    on Signomials  :math:`s_g` and :math:`z_g` are coniclifts Variables of a dimension
    determined by ``p``.

    Parameters
    ----------
    f : Signomial
        The objective in a desired minimization problem.
    gts : list of Signomials
        For every ``g in gts``, there is a desired constraint that variables ``x`` satisfy ``g(x) >= 0``.
    eqs : list of Signomials
        For every ``g in eqs``, there is a desired constraint that variables ``x`` satisfy ``g(x) == 0``.
    p : int
        Controls the complexity of ``s_g`` and ``z_g``.
    q : int
        The number of folds of constraints ``gts`` and ``eqs``.

    Returns
    -------
    L : Signomial
        ``L.c`` is an affine expression of coniclifts Variables.

    ineq_dual_sigs : a list of pairs of Signomial objects.
        If the pair ``(s_g, g)`` is in this list, then ``s_g`` is a generalized Lagrange multiplier
        to the constraint ``g(x) >= 0``.

    eq_dual_sigs : a list of pairs of Signomial objects.
        If the pair ``(z_g, g)`` is in this list, then ``z_g`` is a generalized Lagrange multiplier to the
        constraint ``g(x) == 0``.

    gamma : coniclifts.Variable.
        In primal-form SAGE relaxations, we want to maximize ``gamma``. In dual form SAGE relaxations,
        ``gamma`` induces an equality constraint.

    Notes
    -----
    The Lagrange multipliers ``s_g`` and ``z_g`` share a common matrix of exponent vectors,
    which we call ``alpha_hat``.

    When ``p = 0``, ``alpha_hat`` consists of a single row, of all zeros. In this case,
    ``s_g`` and ``z_g`` are constant Signomials, and the coefficient vectors ``s_g.c``
    and ``z_g.c`` are effectively scalars. When ``p > 0``, the rows of ``alpha_hat`` are
    set to all ``p``-wise sums  of exponent vectors appearing in either ``f``, or some
    ``g in gts``,  or some ``g in eqs``.
    """
    folded_gt = con_gen.up_to_q_fold_cons(gts, q)
    gamma = cl.Variable(name='gamma')
    L = f - gamma
    alpha_E_p = hierarchy_e_k([L] + list(gts) + list(eqs), k=p)
    ineq_dual_sigs = []
    summands = [L]
    for g in folded_gt:
        s_g_coeff = cl.Variable(name='s_' + str(g),
                                shape=(alpha_E_p.shape[0], ))
        s_g = Signomial(alpha_E_p, s_g_coeff)
        summands.append(-g * s_g)
        ineq_dual_sigs.append((s_g, g))
    eq_dual_sigs = []
    folded_eq = con_gen.up_to_q_fold_cons(eqs, q)
    for g in folded_eq:
        z_g_coeff = cl.Variable(name='z_' + str(g),
                                shape=(alpha_E_p.shape[0], ))
        z_g = Signomial(alpha_E_p, z_g_coeff)
        summands.append(-g * z_g)
        eq_dual_sigs.append((z_g, g))
    L = Signomial.sum(summands)
    return L, ineq_dual_sigs, eq_dual_sigs, gamma
Exemplo n.º 2
0
    def __call__(self, x):
        """
        Parameters
        ----------
        x : ndarray
            This vector can contain real numeric types and Polynomial objects.

        Returns
        -------
        val : float or Polynomial
            If ``x`` is purely numeric, then ``val`` is a float.
            If ``x`` contains Polynomial objects, then ``val`` is a Polynomial object.

        Examples
        --------
        Evaluating a Polynomial at a numeric point. ::

            p = Polynomial.from_dict({(1,2,3): -1})
            x = np.array([3,2,1])
            print(p(x))  #  (3 ** 1) * (2 ** 2) * (1 ** 3) * (-1) == -12

        Evaluating a Polynomial on another polynomial. ::

            p = Polynomial.from_dict({(2,): 1})  # represents lambda x: x ** 2
            z = Polynomial.from_dict({(1,): 2, (0,): -1})  # represents lambda x: 2*x - 1
            w = p(z)  # represents lambda x: (2*x - 1) ** 2
            print(w(0.5))  # equals zero
            print(w(1))  # equals one
            print(w(0))  # equals one

        Notes
        -----
        If ``x`` contains Polynomial entries, then those Polynomials must all be over the same
        number of variables. However, those Polynomials need not have the same number of variables
        as the current polynomial.

        """
        if not hasattr(x, 'shape'):
            x = np.array([x])
        if x.ndim == 0:
            x = x.ravel()
        if not x.shape[0] == self.n:
            msg = 'Domain is R^' + str(self._n) + 'but x is in R^' + str(
                x.shape[0])
            raise ValueError(msg)
        xi_func = None
        if x.dtype == 'object':
            func_xis = [xi for xi in x.flat if isinstance(xi, Signomial)]
            ns = np.array([xi.n for xi in func_xis])
            if ns.size == 0:
                x = x.astype(np.float)
            elif np.any(ns != ns[0]):
                msg = 'The input vector cannot contain functions over different variables.'
                raise ValueError(msg)
            else:
                xi_func = func_xis[0]
        if x.ndim == 1:
            x = x.ravel()
            temp1 = np.power(x, self.alpha)
            temp2 = np.prod(temp1, axis=1)
            temp3 = self.c * temp2
            if temp3.dtype != 'object':
                val = np.sum(temp3)
                return val
            else:
                if isinstance(xi_func, Polynomial):
                    temp3 = [xi_func.upcast_to_polynomial(ti) for ti in temp3]
                else:
                    temp3 = [xi_func.upcast_to_signomial(ti) for ti in temp3]
                val = Signomial.sum(temp3)
                return val
        elif x.ndim == 2:
            vals = [self.__call__(xi) for xi in x.T]
            val = np.array(vals)
            return val
        else:
            raise ValueError('Can only evaluate on x with dimension <= 2.')