def literal_to_bounds(f: FNode): """ obtain bounds on variable x from inequality atom :param literal: ax + by + c < dx + ey + f :param x: variable :return: symbolic bound for x, is_lower, k_inclduded for example, given literal 3x + 2y + 4 < x + y + 1, first move all terms to left hand side: 2x + y + 3 < 0, then it returns [-1/2 y - 3/2, False] since the literal is equivalent to x < -1/2 y - 3/2 """ assert (is_literal(f)) assert f.is_le() or f.is_lt() variables = list(get_real_variables(f)) k_included = f.is_le() f = simplify(f) lhs, rhs = f.arg(0), f.arg(1) lhs_coef, lhs_const = get_coefficients(lhs), get_constants(lhs) rhs_coef, rhs_const = get_coefficients(rhs), get_constants(rhs) # move all terms to lhs const = simplify(lhs_const - rhs_const) coef = dict() for v in variables: coef[v] = simplify(lhs_coef[v] - rhs_coef[v]) bounds = dict() for v in variables: if float(coef[v].constant_value()) == 0: continue bound_terms = [simplify(const * Real(-1) / coef[v])] for w in variables: if w == v or float(coef[w].constant_value()) == 0: continue new_w_coef = simplify(coef[w] * Real(-1) / coef[v]) bound_terms.append(Times(new_w_coef, w)) bound = Plus(bound_terms) is_lower = coef[v].constant_value() < 0 bounds[v] = bound, is_lower, k_included return bounds
def atom_to_bound(atom: FNode, x_symbol: FNode): """ obtain bounds on variable x from inequality atom :param atom: must be of plus form or a single term on each side :param x_symbol: :return: """ assert atom.is_le() or atom.is_lt() variables = list(get_real_variables(atom)) if x_symbol not in variables: return [atom, NEITHER] lhs, rhs = atom.arg(0), atom.arg(1) lhs_coef, lhs_const = get_coefficients(lhs), get_constants(lhs) rhs_coef, rhs_const = get_coefficients(rhs), get_constants(rhs) lhs_x_coef = lhs_coef[x_symbol] if lhs_coef.get(x_symbol) else Real(0) rhs_x_coef = rhs_coef[x_symbol] if rhs_coef.get(x_symbol) else Real(0) x_coef_diff = simplify(lhs_x_coef - rhs_x_coef) assert float(x_coef_diff.constant_value()) != 0 flag = simplify(x_coef_diff > 0) bound = simplify((rhs_const - lhs_const) / x_coef_diff) bound_type = UPPER if flag.is_true() else LOWER if len(variables) == 1: return [bound, bound_type] y_symbol = variables[0] if x_symbol == variables[1] else variables[1] lhs_y_coef = lhs_coef[y_symbol] if lhs_coef.get(y_symbol) else Real(0) rhs_y_coef = rhs_coef[y_symbol] if rhs_coef.get(y_symbol) else Real(0) y_coef_diff = simplify(lhs_y_coef - rhs_y_coef) if float(y_coef_diff.constant_value()) == 0: return [bound, bound_type] new_y_coef = simplify(y_coef_diff * Real(-1) / x_coef_diff) bound = Plus(bound, Times(new_y_coef, y_symbol)) return [bound, bound_type]