Beispiel #1
0
 def __init__(self, lhs, rhs, operator):
     from sageopt.coniclifts.base import Expression
     self.id = ElementwiseConstraint._ELEMENTWISE_CONSTRAINT_ID_
     ElementwiseConstraint._ELEMENTWISE_CONSTRAINT_ID_ += 1
     if not isinstance(lhs, Expression):
         lhs = Expression(lhs)
     if not isinstance(rhs, Expression):
         rhs = Expression(rhs)
     self.lhs = lhs
     self.rhs = rhs
     self.initial_operator = operator
     name_str = 'Elementwise[' + str(self.id) + '] : '
     self.name = name_str
     if operator == '==':
         self.expr = (self.lhs - self.rhs).ravel()
         if ElementwiseConstraint._CURVATURE_CHECK_ and not self.expr.is_affine(
         ):
             raise RuntimeError('Equality constraints must be affine.')
         self.operator = '=='  # now we are a linear constraint "self.expr == 0"
         self.epigraph_checked = True
     else:  # elementwise inequality.
         if operator == '>=':
             self.expr = (self.rhs - self.lhs).ravel()
         else:
             self.expr = (self.lhs - self.rhs).ravel()
         if ElementwiseConstraint._CURVATURE_CHECK_ and not all(
                 self.expr.is_convex()):
             raise RuntimeError('Cannot canonicalize.')
         self.operator = '<='  # now we are a convex constraint "self.expr <= 0"
         self.epigraph_checked = False
Beispiel #2
0
def _align_args(x, y):
    if not isinstance(x, Expression):
        x = Expression(x)
    if not isinstance(y, Expression):
        y = Expression(y)
    x = x.ravel()
    y = y.ravel()
    if x.size != y.size:
        raise RuntimeError('Illegal arguments to sum_relent.')
    return x, y
 def test_ordinary_sage_dual_3(self):
     # provide a vector "c" in the dual SAGE cone constructor.
     # generate a point with zero distance from the dual SAGE cone
     n, m = 2, 6
     np.random.seed(0)
     alpha = 10 * np.random.randn(m, n)
     x0 = np.random.randn(n) / 10
     v0 = np.exp(alpha @ x0)
     dummy_vars = Variable(shape=(2, )).scalar_variables()
     c = np.array([1, 2, 3, 4, dummy_vars[0], dummy_vars[1]])
     c = Expression(c)
     v = Variable(shape=(m, ), name='projected_v0')
     t = Variable(shape=(1, ), name='epigraph_var')
     sage_constraint = sage_cones.DualSageCone(v,
                                               alpha,
                                               X=None,
                                               name='test_con',
                                               c=c)
     epi_constraint = vector2norm(v - v0) <= t
     constraints = [sage_constraint, epi_constraint]
     prob = Problem(CL_MIN, t, constraints)
     prob.solve(solver='ECOS')
     viol = sage_constraint.violation(norm_ord=1, rough=False)
     assert viol < 1e-6
     viol = sage_constraint.violation(norm_ord=np.inf, rough=True)
     assert viol < 1e-6
     val = prob.value
     assert val < 1e-7
Beispiel #4
0
 def __init__(self, sense, objective, constraints, **kwargs):
     self.objective_sense = sense
     if not isinstance(objective, Expression):
         objective = Expression(objective)
     self.objective_expr = objective
     self.constraints = constraints
     self.timings = dict()
     t = time.time()
     c, A, b, K, variable_map, all_vars = compile_problem(objective, constraints)
     if sense == CL_CONSTANTS.minimize:
         self.c = c
     else:
         self.c = -c
     self.timings['compile_time'] = time.time() - t
     self.A = A
     self.b = b
     self.K = K
     self.all_variables = all_vars
     self.variable_map = variable_map
     self.variable_values = dict()
     self.solver_apply_data = dict()
     self.solver_raw_output = dict()
     self.status = None  # "solved", "inaccurate", or "failed"
     self.value = np.NaN
     self.metadata = dict()
     self.problem_options = {'cache_apply_data': False,
                             'cache_raw_output': False, 'verbose': True}
     self.problem_options.update(kwargs)
     self._integer_indices = None
     if 'integer_variables' in kwargs:
         self._parse_integer_constraints(kwargs['integer_variables'])
     pass
Beispiel #5
0
def weighted_sum_exp(c, x):
    """
    Return a coniclifts Expression of size 1, representing the signomial

        sum([ci * e^xi for (ci, xi) in (c, x)])

    :param c: a numpy ndarray of nonnegative numbers.
    :param x: a coniclifts Expression of the same size as c.
    """
    if not isinstance(x, Expression):
        x = Expression(x)
    if not isinstance(c, np.ndarray):
        c = np.array(c)
    if np.any(c < 0):
        raise RuntimeError(
            'Epigraphs of non-constant signomials with negative terms are not supported.'
        )
    if x.size != c.size:
        raise RuntimeError('Incompatible arguments.')
    x = x.ravel()
    c = c.ravel()
    kvs = []
    for i in range(x.size):
        if c[i] != 0:
            kvs.append((Exponential(x[i]), c[i]))
    d = dict(kvs)
    se = ScalarExpression(d, 0, verify=False)
    expr = se.as_expr()
    return expr
 def test_separate_cone_constraints_2(self):
     num_vars = 5
     x = Variable(shape=(num_vars, ))
     cons = [vector2norm(x) <= 1]
     prob = Problem(CL_MIN, Expression([0]), cons)
     A0, b0, K0 = prob.A, prob.b, prob.K
     assert A0.shape == (num_vars + 2, num_vars + 1)
     assert len(K0) == 2
     assert K0[0].type == '+' and K0[0].len == 1
     assert K0[1].type == 'S' and K0[1].len == num_vars + 1
     A1, b1, K1, sepK1 = separate_cone_constraints(A0,
                                                   b0,
                                                   K0,
                                                   dont_sep={'+'})
     A1 = A1.toarray()
     assert A1.shape == (num_vars + 2, 2 * (num_vars + 1))
     assert len(K1) == 2
     assert K1[0].type == '+' and K1[0].len == 1
     assert K1[1].type == '0' and K1[1].len == num_vars + 1
     assert len(sepK1) == 1
     assert sepK1[0].type == 'S' and sepK1[0].len == num_vars + 1
     A0 = A0.toarray()
     temp = np.vstack(
         (np.zeros(shape=(1, num_vars + 1)), np.eye(num_vars + 1)))
     expect_A1 = np.hstack((A0, -temp))
     assert np.allclose(expect_A1, A1)
Beispiel #7
0
 def __init__(self, c, alpha, X, name, **kwargs):
     self.settings = SETTINGS.copy()
     covers = kwargs['covers'] if 'covers' in kwargs else None
     if 'settings' in kwargs:
         self.settings.update(kwargs['settings'])
     self._n = alpha.shape[1]
     self._m = alpha.shape[0]
     self.name = name
     self.alpha = alpha
     self.X = X
     self.c = Expression(c)
     if X is not None:
         check_cones(X.K)
         self._lifted_n = X.A.shape[1]
         self.ech = ExpCoverHelper(self.alpha, self.c, (X.A, X.b, X.K), covers, self.settings)
     else:
         self._lifted_n = self._n
         self.ech = ExpCoverHelper(self.alpha, self.c, None, covers, self.settings)
     self.age_vectors = dict()
     self._sfx = None  # "suppfunc x"; for evaluating support function
     self._age_witnesses = None
     self._nus = dict()
     self._pre_nus = dict()  # if kernel_basis == True, then store certain Variables here.
     self._nu_bases = dict()
     self._c_vars = dict()
     self._relent_epi_vars = dict()
     self._eta_vars = dict()
     self._variables = self.c.variables()
     if self._m > 1 and self.X is None:
         self._ordsage_init_variables()
     elif self._m > 1:
         self._condsage_init_variables()
     self._build_aligned_age_vectors()
     pass
Beispiel #8
0
def matvec_plus_vec_times_scalar(mat1, vec1, vec2, scalar):
    # TODO: fix docstring
    """

    :param mat1: a numpy ndarray of shape (m, n).
    :param vec1: a coniclifts Variable of shape (n,) or (n, 1)
    :param vec2: a numpy ndarray of shape (m,) or (m, 1).
    :param scalar: a coniclifts Variable of size 1.
    :return:

    Return A_rows, A_cols, A_vals as if they were generated by a compile() call on
    con = (mat @ vecvar + vec * singlevar >= 0).
    """
    if isinstance(scalar, Expression):
        if scalar.size == 1:
            scalar = scalar.item()
        else:
            raise ValueError('Argument `scalar` must have size 1.')
    # We can now assume that "scalar" is a ScalarExpression.
    if isinstance(vec1, Variable):
        b = scalar.offset * vec2
        a2c = scalar.atoms_to_coeffs.items()
        s_covec = np.array([co for (sv, co) in a2c])
        s_indices = [sv.id for (sv, co) in a2c]
        mat2 = np.outer(vec2, s_covec)  # rank 1
        mat = np.hstack((mat1, mat2))
        indices = vec1.scalar_variable_ids + s_indices
        A_vals, A_rows, A_cols = _matvec_by_var_indices(mat, indices)
    else:
        mat = np.hstack([mat1, np.reshape(vec2, (-1, 1))])
        if isinstance(scalar, ScalarExpression):
            scalar = Expression([scalar])
        expr = concatenate((vec1, scalar))
        A_vals, A_rows, A_cols, b = matvec(mat, expr)
    return A_vals, A_rows, A_cols, b
Beispiel #9
0
 def age_witnesses(self):
     if self._age_witnesses is None:
         self._age_witnesses = dict()
         if self._m > 1:
             for i in self.ech.U_I:
                 wi_expr = Expression(np.zeros(self._m,))
                 num_cover = self.ech.expcover_counts[i]
                 if num_cover > 0:
                     x_i = self._nus[i]
                     wi_expr[self.ech.expcovers[i]] = pos_operator(x_i, eval_only=True)
                     wi_expr[i] = -aff.sum(wi_expr[self.ech.expcovers[i]])
                 self._age_witnesses[i] = wi_expr
         else:
             self.age_vectors[0] = self.c
             self._age_witnesses[0] = Expression([0])
     return self._age_witnesses
Beispiel #10
0
    def project(item, alpha):
        """
        Calculates the shortest distance (the projection) of a vector
        to a cone parametrized by :math:'\\alpha'

        Parameters
        ----------
        item - the point we are projecting
        alpha - the :math:'\\alpha' parameter
                for the Cone that we are projecting to

        Returns
        -------
        The distance of the projection to the Cone
        """
        from sageopt.coniclifts import MIN as CL_MIN

        item = Expression(item).ravel()
        w = Variable(shape=(item.size, ))
        t = Variable(shape=(1, ))

        cons = [vector2norm(item - w) <= t, PowCone(w, alpha)]

        prob = Problem(CL_MIN, t, cons)
        prob.solve(verbose=False)

        return prob.value
Beispiel #11
0
 def __init__(self, v, alpha, X, name, **kwargs):
     self.settings = SETTINGS.copy()
     if 'settings' in kwargs:
         self.settings.update(kwargs['settings'])
     covers = kwargs['covers'] if 'covers' in kwargs else None
     c = kwargs['c'] if 'c' in kwargs else None
     self.alpha = alpha
     self.c = Expression(c) if c is not None else None
     self.v = v
     self._n = alpha.shape[1]
     self._m = alpha.shape[0]
     if X is not None:
         check_cones(X.K)
         self._lifted_n = X.A.shape[1]
         self.ech = ExpCoverHelper(self.alpha, self.c, (X.A, X.b, X.K), covers, self.settings)
     else:
         self._lifted_n = self._n
         self.ech = ExpCoverHelper(self.alpha, self.c, None, covers, self.settings)
     self.X = X
     self.mu_vars = dict()
     self.name = name
     self._lifted_mu_vars = dict()
     self._relent_epi_vars = dict()
     self._initialize_variables()
     pass
Beispiel #12
0
def sum_relent(x, y, z, aux_vars):
    # represent "sum{x[i] * ln( x[i] / y[i] )} + z <= 0" in conic form.
    # return the Variable object created for all epigraphs needed in
    # this process, as well as A_data, b, and K.
    x, y = _align_args(x, y)
    if not isinstance(z, Expression):
        z = Expression(z)
    if z.size != 1 or not z.is_affine():
        raise RuntimeError('Illegal argument to sum_relent.')
    else:
        z = z.item()  # gets the single element
    num_rows = 1 + 3 * x.size
    b = np.zeros(num_rows,)
    K = [Cone('+', 1)] + [Cone('e', 3) for _ in range(x.size)]
    A_rows, A_cols, A_vals = [], [], []
    # populate the first row
    z_id2co = [(a.id, co) for a, co in z.atoms_to_coeffs.items()]
    A_cols += [aid for aid, _ in z_id2co]
    A_vals += [-co for _, co in z_id2co]
    aux_var_ids = aux_vars.scalar_variable_ids
    A_cols += aux_var_ids
    A_vals += [-1] * len(aux_var_ids)
    A_rows += [0] * len(A_vals)
    b[0] = -z.offset
    # populate the epigraph terms
    curr_row = 1
    _fast_elemwise_data(A_rows, A_cols, A_vals, b, x, y, aux_var_ids, curr_row)
    return A_vals, np.array(A_rows), A_cols, b, K
 def test_separate_cone_constraints_1(self):
     num_ineqs = 10
     num_vars = 5
     G = np.random.randn(num_ineqs, num_vars).round(decimals=3)
     x = Variable(shape=(num_vars, ))
     h = np.random.randn(num_ineqs).round(decimals=3)
     cons = [G @ x >= h]
     prob = Problem(CL_MIN, Expression([0]), cons)
     A0, b0, K0 = prob.A, prob.b, prob.K
     # main test (separate everything other than the zero cone)
     A1, b1, K1, sepK1 = separate_cone_constraints(A0, b0, K0)
     A1 = A1.toarray()
     assert A1.shape == (num_ineqs, num_vars + num_ineqs)
     expect_A1 = np.hstack((G, -np.eye(num_ineqs)))
     assert np.allclose(A1, expect_A1)
     assert len(K1) == 1
     assert K1[0].type == '0'
     assert len(sepK1) == 1
     assert sepK1[0].type == '+'
     assert np.allclose(b0, b1)
     assert np.allclose(b0, -h)
     # trivial test (don't separate anything, including some cones not in the set)
     A2, b2, K2, sepK2 = separate_cone_constraints(
         A0, b0, K0, dont_sep={'+', '0', 'S', 'e'})
     A2 = A2.toarray()
     A0 = A0.toarray()
     assert np.allclose(A0, A2)
     assert np.allclose(b0, b2)
     pass
Beispiel #14
0
    def violation(self, norm_ord=np.inf, rough=False):
        """
        Return a measure of violation for the constraint that ``self.v`` belongs to
        :math:`C_{\\mathrm{SAGE}}(\\alpha, X)^{\\dagger}`.

        Parameters
        ----------
        norm_ord : int
            The value of ``ord`` passed to numpy ``norm`` functions, when reducing
            vector-valued residuals into a scalar residual.

        rough : bool
            Setting ``rough=False`` computes violation by solving an optimization
            problem. Setting ``rough=True`` computes violation by taking norms of
            residuals of appropriate elementwise equations and inequalities involving
            ``self.v`` and auxiliary variables.

        Notes
        -----
        When ``rough=False``, the optimization-based violation is computed by projecting
        the vector ``self.v`` onto a new copy of a dual SAGE constraint, and then returning
        the L2-norm between ``self.v`` and that projection. This optimization step essentially
        re-solves for all auxiliary variables used by this constraint.
        """
        v = self.v.value
        viols = []
        for i in self.ech.U_I:
            selector = self.ech.expcovers[i]
            num_cover = self.ech.expcover_counts[i]
            if num_cover > 0:
                expr1 = np.tile(v[i], num_cover).ravel()
                expr2 = v[selector].ravel()
                lowerbounds = special_functions.rel_entr(expr1, expr2)
                mat = -(self.alpha[selector, :] - self.alpha[i, :])
                mu_i = self._lifted_mu_vars[i].value
                # compute rough violation for this dual AGE cone
                residual = mat @ mu_i[:self._n] - lowerbounds
                residual[residual >= 0] = 0
                curr_viol = np.linalg.norm(residual, ord=norm_ord)
                if (self.X is not None) and (not np.isnan(curr_viol)):
                    AbK_val = self.X.A @ mu_i + v[i] * self.X.b
                    AbK_viol = PrimalProductCone.project(AbK_val, self.X.K)
                    curr_viol += AbK_viol
                # as applicable, solve an optimization problem to compute the violation.
                if (curr_viol > 0 or np.isnan(curr_viol)) and not rough:
                    temp_var = Variable(shape=(self._lifted_n,), name='temp_var')
                    cons = [mat @ temp_var[:self._n] >= lowerbounds]
                    if self.X is not None:
                        con = PrimalProductCone(self.X.A @ temp_var + v[i] * self.X.b, self.X.K)
                        cons.append(con)
                    prob = Problem(CL_MIN, Expression([0]), cons)
                    status, value = prob.solve(verbose=False)
                    if status in {CL_SOLVED, CL_INACCURATE} and abs(value) < 1e-7:
                        curr_viol = 0
                viols.append(curr_viol)
            else:
                viols.append(0)
        viol = max(viols)
        return viol
Beispiel #15
0
def relent(x, y, elementwise=False):
    if not isinstance(x, Expression):
        x = Expression(x)
    if not isinstance(y, Expression):
        y = Expression(y)
    if x.size != y.size:
        raise RuntimeError('Incompatible arguments to relent.')
    if elementwise:
        expr = np.empty(shape=x.shape, dtype=object)
        for tup in array_index_iterator(expr.shape):
            expr[tup] = ScalarExpression({RelEnt(x[tup], y[tup]): 1}, 0)
        return expr.view(Expression)
    else:
        x = x.ravel()
        y = y.ravel()
        d = dict((RelEnt(x[i], y[i]), 1) for i in range(x.size))
        return ScalarExpression(d, 0).as_expr()
Beispiel #16
0
 def project(item, K):
     from sageopt.coniclifts import MIN as CL_MIN
     item = Expression(item).ravel()
     x = Variable(shape=(item.size, ))
     t = Variable(shape=(1, ))
     cons = [vector2norm(item - x) <= t, PrimalProductCone(x, K)]
     prob = Problem(CL_MIN, t, cons)
     prob.solve(verbose=False)
     return prob.value
Beispiel #17
0
    def __init__(self, w, lamb):

        self.id = PowCone._CONSTRAINT_ID_
        PowCone._CONSTRAINT_ID_ += 1

        # Finds indices of all positive elements of lambs (alpha)
        pos_idxs = lamb > 0

        # Error if lamb and w are not same size
        if not w.size == lamb.size:
            msg = 'Incompatible dimensions for w (%s) and alpha (%s)' % (
                w.size, lamb.size)
            raise ValueError(msg)

        # Error if alpha has no negative number
        if np.all(pos_idxs):
            msg = 'No negative number in inputted lamb array'
            raise ValueError(msg)

        neg_idxs = lamb < 0
        if np.abs(np.sum(lamb)) > 1e-6:
            msg = 'lamb does not have sum of 0'
            raise ValueError(msg)

        # Get positive values and normalize
        alpha_low = lamb[pos_idxs] / np.abs(lamb[neg_idxs])

        # Negative number corresponds to z in power cone
        # and rest of numpy array is w
        w_low = w[pos_idxs]
        z_low = w[neg_idxs]

        if not isinstance(w_low, Expression):
            w_low = Expression(w_low)
        if not isinstance(z_low, Expression):
            z_low = Expression(z_low)

        self.w = w
        self.lamb = lamb
        self.w_low = w_low
        self.z_low = z_low
        self.alpha = alpha_low
        pass
Beispiel #18
0
 def __init__(self, y, K):
     self.id = PrimalProductCone._CONSTRAINT_ID_
     PrimalProductCone._CONSTRAINT_ID_ += 1
     if not isinstance(y, Expression):
         y = Expression(y)
     K_size = sum([co.len for co in K])
     if not y.size == K_size:
         raise RuntimeError('Incompatible dimensions for y (' + str(y.size) + ') and K (' + str(K_size) + ')')
     if np.any([co.type == 'P' for co in K]):
         raise RuntimeError('This function does not currently support the PSD cone.')
     self.y = y.ravel()
     self.K = K
     self.K_size = K_size
     pass
Beispiel #19
0
 def _build_aligned_age_vectors(self):
     if self._m > 1:
         for i in self.ech.U_I:
             ci_expr = Expression(np.zeros(self._m,))
             if i in self.ech.N_I:
                 ci_expr[self.ech.expcovers[i]] = self._c_vars[i]
                 ci_expr[i] = self.c[i]
             else:
                 ci_expr[self.ech.expcovers[i]] = self._c_vars[i][:-1]
                 ci_expr[i] = self._c_vars[i][-1]
             self.age_vectors[i] = ci_expr
     else:
         self.age_vectors[0] = self.c
     pass
Beispiel #20
0
def abs(x, eval_only=False):
    """
    Return a coniclifts Expression representing |x| componentwise.

    :param x: a coniclifts Expression.

    :param eval_only: bool. True if the returned Expression will not be used
    in an optimization problem.
    """
    if not isinstance(x, Expression):
        x = Expression(x)
    expr = np.empty(shape=x.shape, dtype=object)
    for tup in array_index_iterator(expr.shape):
        expr[tup] = ScalarExpression({Abs(x[tup], eval_only): 1}, 0, verify=False)
    return expr.view(Expression)
Beispiel #21
0
def create_covers(s):
    covers = dict()
    c = Expression(s.c)
    # determine which AGE cones are needed
    for i in range(s.m):
        if c[i].is_constant() and c[i].value >= 0 and np.all(s.alpha[i, :] %
                                                             2 == 0):
            pass
        else:
            covers[i] = np.full(shape=c.size, fill_value=True, dtype=bool)
            covers[i][i] = False
    # if a monomial isn't even, then it can't participate in any cover.
    for j in range(s.m):
        if np.any(s.alpha[j, :] % 2 != 0):
            for cover in covers.values():
                cover[j] = False
    return covers
Beispiel #22
0
 def conic_form(self):
     self_dual_cones = {'+', 'S', 'P'}
     start_row = 0
     y_mod = []
     for co in self.K:
         stop_row = start_row + co.len
         if co.type in self_dual_cones:
             y_mod.append(self.y[start_row:stop_row])
         elif co.type == 'e':
             temp_y = np.array([
                 -self.y[start_row + 2],
                 np.exp(1) * self.y[start_row + 1], -self.y[start_row]
             ])
             y_mod.append(temp_y)
         elif co.type != '0':
             raise RuntimeError('Unexpected cone type "%s".' % str(co.type))
         start_row = stop_row
     y_mod = np.hstack(y_mod)
     y_mod = Expression(y_mod)
     # Now we can pretend all nonzero cones are self-dual.
     A_vals, A_rows, A_cols = [], [], []
     cur_K = [Cone(co.type, co.len) for co in self.K if co.type != '0']
     cur_K_size = sum([co.len for co in cur_K])
     if cur_K_size > 0:
         b = np.zeros(shape=(cur_K_size, ))
         for i, se in enumerate(y_mod):
             if len(se.atoms_to_coeffs) == 0:
                 b[i] = se.offset
                 A_rows.append(i)
                 A_cols.append(
                     int(ScalarVariable.curr_variable_count()) - 1)
                 A_vals.append(
                     0
                 )  # make sure scipy infers correct dimensions later on.
             else:
                 b[i] = se.offset
                 A_rows += [i] * len(se.atoms_to_coeffs)
                 col_idx_to_coeff = [(a.id, c)
                                     for a, c in se.atoms_to_coeffs.items()]
                 A_cols += [atom_id for (atom_id, _) in col_idx_to_coeff]
                 A_vals += [c for (_, c) in col_idx_to_coeff]
         return [(A_vals, np.array(A_rows), A_cols, b, cur_K)]
     else:
         return [([], np.zeros(shape=(0, ),
                               dtype=int), [], np.zeros(shape=(0, )), [])]
Beispiel #23
0
 def _presolve_trivial_ord_age(self, i, covers):
     """
     Set covers[i][:] = False if the i-th (ordinary) AGE cone is trivial.
     """
     mat = self.alpha[covers[i], :] - self.alpha[i, :]
     x = Variable(shape=(mat.shape[1], ), name='temp_x')
     objective = Expression([0])
     cons = [mat @ x <= -1]
     prob = Problem(CL_MIN, objective, cons)
     # If prob is feasible, then there exists a sequence x_t where
     # max(mat @ x_t) diverges to -\infty as t increases. Using this
     # sequence we can satisfy the constraints for the i-th dual
     # AGE cone no matter the value of the vector "v" that needs to belong
     # to the dual AGE cone.
     prob.solve(verbose=False, solver=self.settings['reduction_solver'])
     if prob.status in {CL_SOLVED, CL_INACCURATE
                        } and abs(prob.value) < 1e-7:
         covers[i][:] = False
Beispiel #24
0
 def __init__(self, y, K):
     # y must belong to K^dagger
     self.id = DualProductCone._CONSTRAINT_ID_
     DualProductCone._CONSTRAINT_ID_ += 1
     if not isinstance(y, Expression):
         y = Expression(y)
     K_size = sum([co.len for co in K])
     if not y.size == K_size:
         raise RuntimeError(
             'Incompatible dimensions for y (%s) and K (%s)' %
             (str(y.size), str(K_size)))
     if np.any([co.type == 'P' for co in K]):
         raise RuntimeError(
             'This function does not currently support the PSD cone.')
     self.y = y.ravel()
     self.K = K
     self.K_size = K_size
     pass
Beispiel #25
0
 def _build_aligned_age_vectors(self):
     if self._m > 1:
         for i in self.ech.U_I:
             ci_expr = Expression(np.zeros(self._m, ))
             raw_age_vec = self._c_vars[i]
             if i in self.ech.N_I:
                 # Then we didn't create a component for age_vectors[i][i]; need to get
                 # that component from c[i].
                 ci_expr[i] = self.c[i]
                 ci_expr[self.ech.covers[i]] = raw_age_vec
             else:
                 # Then we did create an AGE cone (i in U_I) and we also created an i-th
                 # variable for it (because maybe age_vectors[i][i] = 0 at optimality, when
                 # c[i] > 0 is needed for some other AGE cone).
                 ci_expr[i] = raw_age_vec[-1]
                 ci_expr[self.ech.covers[i]] = raw_age_vec[:-1]
             self.age_vectors[i] = ci_expr
     else:
         self.age_vectors[0] = self.c
     pass
Beispiel #26
0
    def _default_exp_covers(self):
        expcovers = dict()
        for i in self.U_I:
            cov = np.ones(shape=(self.m,), dtype=bool)
            cov[self.N_I] = False
            cov[i] = False
            expcovers[i] = cov
        if self.AbK is None or self.settings['heuristic_reduction']:
            row_sums = np.sum(self.alpha, 1)
            if np.all(self.alpha >= 0) and np.min(row_sums) == 0:
                # Then apply the reduction.
                zero_loc = np.nonzero(row_sums == 0)[0][0]
                for i in self.U_I:
                    if i == zero_loc:
                        continue
                    curr_cover = expcovers[i]
                    curr_row = self.alpha[i, :]
                    for j in range(self.m):
                        if curr_cover[j] and j != zero_loc and curr_row @ self.alpha[j, :] == 0:
                            curr_cover[j] = False
                            """
                            The above operation is without loss of generality for ordinary SAGE
                            constraints. For conditional SAGE constraints, the operation may or
                            may-not be without loss of generality. As a basic check, the above
                            operation is w.l.o.g. even for conditional SAGE constraints, as long
                            as the "conditioning" satisfies the following property:

                                Suppose "y" a geometric-form solution which is feasible w.r.t.
                                conditioning. Then "y" remains feasible (w.r.t. conditioning)
                                when we assign "y[k] = 0".

                            The comments below explain in greater detail.

                            Observation
                            -----------
                            By being in this part of the code, there must exist a "k" where

                                 alpha[i,k] == 0 and alpha[j,k] > 0.

                            Also, we have alpha >= 0. These facts tell us that the expression

                                (alpha[j2,:] - alpha[i,:]) @ mu[:, i] (*)

                            is (1) non-decreasing in mu[k,i] for all 0 <= j2 < m, and (2)
                            strictly increasing in mu[k,i] when j2 == j. Therefore by
                            sending mu[i,k] to -\infty, we do not increase (*) for any
                            0 <= j2 < m, and in fact (*) goes to -\infty for j2 == j.

                            Consequence 1
                            -------------
                            If mu[:,i] is only subject to constraints of the form

                                v[i]*log(v[j2]/v[i]) >= (alpha[j2,:] - alpha[i,:]) @ mu[:, i]

                            with 0 <= j2 < m, then the particular constraint with j2 == j
                            is never active at any optimal solution. For ordinary SAGE cones,
                            this means the j-th term of alpha isn't used in the i-th AGE cone.

                            Consequence 2
                            -------------
                            For conditional SAGE cones, there is another constraint:

                                 A @ mu[:, i] + v[i] * b \in K.      (**)

                            However, as long as (**) allows us to send mu[k,i] to -\infty
                            without affecting feasibility, then the we arrive at the same
                            conclusion: the j-th term of alpha isn't used in the i-th AGE cone.
                            """
        if self.AbK is None:
            for i in self.U_I:
                if np.count_nonzero(expcovers[i]) == 1:
                    expcovers[i][:] = False
        if self.settings['presolve_trivial_age_cones']:
            if self.AbK is None:
                for i in self.U_I:
                    if np.any(expcovers[i]):
                        mat = self.alpha[expcovers[i], :] - self.alpha[i, :]
                        x = Variable(shape=(mat.shape[1],), name='temp_x')
                        objective = Expression([0])
                        cons = [mat @ x <= -1]
                        prob = Problem(CL_MIN, objective, cons)
                        prob.solve(verbose=False,
                                   solver=self.settings['reduction_solver'])
                        if prob.status in {CL_SOLVED, CL_INACCURATE} and abs(prob.value) < 1e-7:
                            expcovers[i][:] = False
            else:
                for i in self.U_I:
                    if np.any(expcovers[i]):
                        mat = self.alpha[expcovers[i], :] - self.alpha[i, :]
                        x = Variable(shape=(mat.shape[1],), name='temp_x')
                        t = Variable(shape=(1,), name='temp_t')
                        objective = t
                        A, b, K = self.AbK
                        cons = [mat @ x <= t, PrimalProductCone(A @ x + b, K)]
                        prob = Problem(CL_MIN, objective, cons)
                        prob.solve(verbose=False,
                                   solver=self.settings['reduction_solver'])
                        if prob.status in {CL_SOLVED, CL_INACCURATE} and prob.value < -100:
                            expcovers[i][:] = False
        return expcovers
Beispiel #27
0
def vector2norm(x):
    if not isinstance(x, Expression):
        x = Expression(x)
    x = x.ravel()
    d = {Vector2Norm(x): 1}
    return ScalarExpression(d, 0).as_expr()
Beispiel #28
0
def cast_res(res):
    if isinstance(res, np.ndarray) and res.dtype == object:
        return Expression(res)  # includes Variables
    else:
        return res  # should only be Expressions, ScalarExpressions, and numeric ndarrays.