示例#1
0
def poly_constrained_dual(f, gts, eqs, p=0, q=1, ell=0, X=None, slacks=False):
    """
    Construct the dual SAGE-(p, q, ell) relaxation for the polynomial optimization problem

        inf{ f(x) : g(x) >= 0 for g in gts,
                    g(x) == 0 for g in eqs,
                    and x in X }

    where :math:`X = R^{\\texttt{f.n}}` by default.
    """
    lagrangian, ineq_lag_mults, eq_lag_mults, _ = make_poly_lagrangian(f, gts, eqs, p=p, q=q)
    metadata = {'lagrangian': lagrangian, 'f': f, 'gts': gts, 'eqs': eqs, 'X': X}
    if ell > 0:
        alpha_E_1 = hierarchy_e_k([f, f.upcast_to_polynomial(1)] + gts + eqs, k=1)
        modulator = Polynomial(2 * alpha_E_1, np.ones(alpha_E_1.shape[0])) ** ell
        lagrangian = lagrangian * modulator
        f = f * modulator
    else:
        modulator = f.upcast_to_polynomial(1)
    metadata['modulator'] = modulator
    # In primal form, the Lagrangian is constrained to be a SAGE polynomial.
    # Introduce a dual variable "v" for this constraint.
    v = cl.Variable(shape=(lagrangian.m, 1), name='v')
    metadata['v_poly'] = v
    constraints = relative_dual_sage_poly_cone(lagrangian, v, 'Lagrangian', log_AbK=X)
    for s_g, g in ineq_lag_mults:
        # These generalized Lagrange multipliers "s_g" are SAGE polynomials.
        # For each such multiplier, introduce an appropriate dual variable "v_g", along
        # with constraints over that dual variable.
        g_m = g * modulator
        c_g = sym_corr.moment_reduction_array(s_g, g_m, lagrangian)
        name_base = 'v_' + str(g)
        if slacks:
            v_g = cl.Variable(name=name_base, shape=(s_g.m, 1))
            con = c_g @ v == v_g
            con.name += str(g) + ' >= 0'
            constraints.append(con)
        else:
            v_g = c_g @ v
        constraints += relative_dual_sage_poly_cone(s_g, v_g,
                                                    name_base=(name_base + ' domain'), log_AbK=X)
    for z_g, g in eq_lag_mults:
        # These generalized Lagrange multipliers "z_g" are arbitrary polynomials.
        # They dualize to homogeneous equality constraints.
        g_m = g * modulator
        c_g = sym_corr.moment_reduction_array(z_g, g_m, lagrangian)
        con = c_g @ v == 0
        con.name += str(g) + ' == 0'
        constraints.append(con)
    # Equality constraint (for the Lagrangian to be bounded).
    a = sym_corr.relative_coeff_vector(modulator, lagrangian.alpha)
    constraints.append(a.T @ v == 1)
    # Define the dual objective function.
    obj_vec = sym_corr.relative_coeff_vector(f, lagrangian.alpha)
    obj = obj_vec.T @ v
    # Return the coniclifts Problem.
    prob = cl.Problem(cl.MIN, obj, constraints)
    prob.metadata = metadata
    cl.clear_variable_indices()
    return prob
示例#2
0
def sig_constrained_dual(f, gts, eqs, p=0, q=1, ell=0, X=None, slacks=False):
    """
    Construct the SAGE-(p, q, ell) dual problem for the signomial program

        min{ f(x) : g(x) >= 0 for g in gts,
                    g(x) == 0 for g in eqs,
                    and x in X }

    where X = :math:`R^{\\texttt{f.n}}` by default.
    """
    lagrangian, ineq_lag_mults, eq_lag_mults, _ = make_sig_lagrangian(f, gts, eqs, p=p, q=q)
    metadata = {'lagrangian': lagrangian, 'f': f, 'gts': gts, 'eqs': eqs, 'level': (p, q, ell), 'X': X}
    if ell > 0:
        alpha_E_1 = hierarchy_e_k([f, f.upcast_to_signomial(1)] + list(gts) + list(eqs), k=1)
        modulator = Signomial(alpha_E_1, np.ones(alpha_E_1.shape[0])) ** ell
        lagrangian = lagrangian * modulator
        f = f * modulator
    else:
        modulator = f.upcast_to_signomial(1)
    metadata['modulator'] = modulator
    # In primal form, the Lagrangian is constrained to be a SAGE signomial.
    # Introduce a dual variable "v" for this constraint.
    v = cl.Variable(shape=(lagrangian.m, 1), name='v')
    con = relative_dual_sage_cone(lagrangian, v, name='Lagrangian SAGE dual constraint', X=X)
    constraints = [con]
    expcovers = None
    for i, (s_h, h) in enumerate(ineq_lag_mults):
        # These generalized Lagrange multipliers "s_h" are SAGE signomials.
        # For each such multiplier, introduce an appropriate dual variable "v_h", along
        # with constraints over that dual variable.
        h_m = h * modulator
        c_h = sym_corr.moment_reduction_array(s_h, h_m, lagrangian)
        if slacks:
            v_h = cl.Variable(name='v_' + str(h), shape=(s_h.m, 1))
            constraints.append(c_h @ v == v_h)
        else:
            v_h = c_h @ v
        con_name = 'SAGE dual for signomial inequality # ' + str(i)
        con = relative_dual_sage_cone(s_h, v_h, name=con_name, X=X, expcovers=expcovers)
        expcovers = con.ech.expcovers  # only * really * needed in first iteration, but keeps code flat.
        constraints.append(con)
    for s_h, h in eq_lag_mults:
        # These generalized Lagrange multipliers "s_h" are arbitrary signomials.
        # They dualize to homogeneous equality constraints.
        h = h * modulator
        c_h = sym_corr.moment_reduction_array(s_h, h, lagrangian)
        constraints.append(c_h @ v == 0)
    # Equality constraint (for the Lagrangian to be bounded).
    a = sym_corr.relative_coeff_vector(modulator, lagrangian.alpha)
    constraints.append(a.T @ v == 1)
    # Define the dual objective function.
    obj_vec = sym_corr.relative_coeff_vector(f, lagrangian.alpha)
    obj = obj_vec.T @ v
    # Return the coniclifts Problem.
    prob = cl.Problem(cl.MIN, obj, constraints)
    prob.metadata = metadata
    cl.clear_variable_indices()
    return prob
示例#3
0
def sig_solrec(prob, ineq_tol=1e-8, eq_tol=1e-6, skip_ls=False):
    """
    Recover a list of candidate solutions from a dual SAGE relaxation. Solutions are
    guaranteed to be feasible up to specified tolerances, but not necessarily optimal.

    Parameters
    ----------
    prob : coniclifts.Problem
        A dual-form SAGE relaxation.
    ineq_tol : float
        The amount by which recovered solutions can violate inequality constraints.
    eq_tol : float
        The amount by which recovered solutions can violate equality constraints.
    skip_ls : bool
        Whether or not to skip constrained least-squares solution recovery.

    Returns
    -------
    sols : list of ndarrays
        A list of feasible solutions, sorted in increasing order of objective function value.
        It is possible that this list is empty, in which case no feasible solutions were recovered.

    """
    con = prob.constraints[0]
    if not con.name == 'Lagrangian SAGE dual constraint':  # pragma: no cover
        raise RuntimeError('Unexpected first constraint in dual SAGE relaxation.')
    metadata = prob.metadata
    f = metadata['f']
    # Recover any constraints present in "prob"
    lag_gts, lag_eqs = [], []
    if 'gts' in metadata:
        # only happens in "constrained_sage_dual".
        lag_gts = metadata['gts']
        lag_eqs = metadata['eqs']
    lagrangian = _make_dummy_lagrangian(f, lag_gts, lag_eqs)
    if con.X is None:
        X_gts, X_eqs = [], []
    else:
        X_gts, X_eqs = con.X.gts, con.X.eqs
    gts = lag_gts + X_gts
    eqs = lag_eqs + X_eqs
    # Search for solutions which meet the feasibility criteria
    v = con.v.value
    v[v < 0] = 0
    if np.any(np.isnan(v)):
        return None
    alpha = con.alpha
    dummy_modulated_lagrangian = Signomial(alpha, np.ones(shape=(alpha.shape[0],)))
    alpha_reduced = lagrangian.alpha
    modulator = metadata['modulator']
    M = moment_reduction_array(lagrangian, modulator, dummy_modulated_lagrangian)
    if skip_ls:
        sols0 = []
    else:
        sols0 = _least_squares_solution_recovery(alpha_reduced, con, v, M, gts, eqs, ineq_tol, eq_tol)
    sols1 = _dual_age_cone_solution_recovery(con, v, M, gts, eqs, ineq_tol, eq_tol)
    sols = sols0 + sols1
    sols.sort(key=lambda mu: f(mu))
    return sols
示例#4
0
def poly_solrec(prob, ineq_tol=1e-8, eq_tol=1e-6, skip_ls=False, **kwargs):
    """
    Recover a list of candidate solutions from a dual SAGE relaxation. Solutions are
    guaranteed to be feasible up to specified tolerances, but not necessarily optimal.

    Parameters
    ----------
    prob : coniclifts.Problem
        A dual-form SAGE relaxation, from ``poly_constrained_relaxation``.

    ineq_tol : float
        The amount by which recovered solutions can violate inequality constraints.

    eq_tol : float
        The amount by which recovered solutions can violate equality constraints.

    skip_ls : bool
        Whether or not to skip least-squares solution recovery.

    Returns
    -------
    sols : list of ndarrays
        A list of feasible solutions, sorted in increasing order of objective function value.
        It is possible that this list is empty, in which case no feasible solutions were recovered.

    Notes
    -----
    This function accepts the following keyword arguments:

    zero_tol : float
        Used in magnitude recovery. If a component of the Lagrangian's moment vector is smaller
        than this (in absolute value), pretend it's zero in the least-squares step. Defaults to 1e-20.

    heuristic_signs : bool
        Used in sign recovery. If True, then attempts to infer variable signs from the Lagrangian's
        moment vector even when a completely consistent set of signs does not exist. Defaults to True.

    all_signs : bool
        Used in sign recovery. If True, then consider returning solutions which differ only by sign.
        Defaults to True.

    This function is implemented only for poly_constrained_relaxation (not poly_relaxation).
    """
    zero_tol = kwargs['zero_tol'] if 'zero_tol' in kwargs else 1e-20
    heuristic = kwargs[
        'heuristic_signs'] if 'heuristic_signs' in kwargs else True
    all_signs = kwargs['all_signs'] if 'all_signs' in kwargs else True
    metadata = prob.metadata
    f = metadata['f']
    lag_gts = metadata['gts']
    lag_eqs = metadata['eqs']
    lagrangian = _make_dummy_lagrangian(f, lag_gts, lag_eqs)
    con = prob.constraints[0]
    alpha = con.alpha
    dummy_modulated_lagrangian = Polynomial(
        alpha, np.ones(shape=(alpha.shape[0], )))  # coefficients dont matter
    modulator = metadata['modulator']
    v = metadata['v_poly'].value  # possible that v_sig and v are the same
    if np.any(np.isnan(v)):
        return []
    M = moment_reduction_array(lagrangian, modulator,
                               dummy_modulated_lagrangian)
    v_reduced = M @ v
    alpha_reduced = lagrangian.alpha
    mags = variable_magnitudes(con, alpha_reduced, v_reduced, zero_tol,
                               skip_ls)
    signs = variable_sign_patterns(alpha_reduced, v_reduced, heuristic,
                                   all_signs)
    # Now we need to build the candidate solutions, and check them for feasibility.
    if con.X is not None:
        gts = lag_gts + [g for g in con.X.gts]
        eqs = lag_eqs + [g for g in con.X.eqs]
    else:
        gts = lag_gts
        eqs = lag_eqs
    solutions = []
    for mag in mags:
        for sign in signs:
            x = mag * sign  # elementwise
            if is_feasible(x, gts, eqs, ineq_tol, eq_tol):
                solutions.append(x)
    solutions.sort(key=lambda xi: f(xi))
    return solutions