Esempio n. 1
0
    def _find_root_node_feasible_solution(self, run_id, problem):
        logger.info(run_id, 'Finding feasible solution at root node')

        if self.root_node_feasible_solution_seed is not None:
            seed = self.root_node_feasible_solution_seed
            logger.info(run_id, 'Use numpy seed {}', seed)
            np.random.seed(seed)

        starting_point = [0.0] * problem.num_variables
        for i, v in enumerate(problem.variables):
            if problem.has_starting_point(v):
                starting_point[i] = problem.starting_point(v)
            elif problem.domain(v).is_integer():
                # Variable is integer and will be fixed, but we don't have a
                # starting point for it. Use lower or upper bound.
                has_lb = is_inf(problem.lower_bound(v))
                has_ub = is_inf(problem.upper_bound(v))
                if has_lb:
                    starting_point[i] = problem.lower_bound(v)
                elif has_ub:
                    starting_point[i] = problem.upper_bound(v)
                else:
                    starting_point[i] = 0
        return solve_primal_with_starting_point(
            run_id, problem, starting_point, self._nlp_solver
        )
Esempio n. 2
0
def compute_branching_point(variable, mip_solution, lambda_):
    """Compute a convex combination of the midpoint and the solution value.

    Given a variable $x_i \in [x_i^L, x_i^U]$, with midpoint
    $x_m = x_i^L + 0.5(x_i^U - x_i^L)$, and its solution value $\bar{x_i}$,
    branch at $\lambda x_m + (1 - \lambda) \bar{x_i}$.

    Parameters
    ----------
    variable : VariableView
        the branching variable
    mip_solution : Solution
        the MILP solution
    lambda_ : float
        the weight
    """
    x_bar = mip_solution.variables[variable.variable.idx].value
    lb = variable.lower_bound()
    ub = variable.upper_bound()

    # Use special bounds if variable is unbounded
    user_upper_bound = mc.user_upper_bound
    if variable.domain.is_integer():
        user_upper_bound = mc.user_integer_upper_bound

    if is_inf(lb):
        lb = -user_upper_bound

    if is_inf(ub):
        ub = user_upper_bound

    midpoint = lb + 0.5 * (ub - lb)
    return lambda_ * midpoint + (1.0 - lambda_) * x_bar
Esempio n. 3
0
    def _get_starting_point_and_bounds(self, problem):
        x0 = np.zeros(problem.num_variables)
        bounds = [(None, None)] * problem.num_variables

        for i, var in enumerate(problem.variables):
            vv = problem.variable_view(var)

            if vv.is_fixed():
                value = vv.value()
                x0[i] = value
                bounds[i] = (value, value)
            else:
                numerical_lb = lb = vv.lower_bound()

                if is_inf(lb):
                    lb = None
                    numerical_lb = -mc.user_upper_bound

                numerical_ub = ub = vv.upper_bound()
                if is_inf(ub):
                    ub = None
                    numerical_ub = mc.user_upper_bound

                bounds[i] = (lb, ub)

                if vv.has_starting_point():
                    starting_point = vv.starting_point()
                    if is_inf(starting_point):
                        starting_point = max(numerical_lb,
                                             min(numerical_ub, 0))
                    x0[i] = starting_point
                else:
                    x0[i] = max(numerical_lb, min(numerical_ub, 0))

        return x0, bounds
Esempio n. 4
0
def _unbounded_variable(problem):
    for var in problem.variables:
        unbounded = (
           is_inf(problem.lower_bound(var)) and
           is_inf(problem.upper_bound(var))
        )
        if unbounded:
            return var
    return None
Esempio n. 5
0
 def add_initial_solution(self, solution, mc):
     assert is_inf(self.lower_bound, mc)
     assert is_inf(self.upper_bound, mc)
     self._update_node(
         self.root,
         solution,
         is_root_node=True,
         update_nodes_visited=False,
     )
     self.root.initial_feasible_solution = solution.upper_bound_solution
Esempio n. 6
0
    def get_starting_point_and_bounds(self, run_id, problem):
        nx = problem.num_variables

        xi = np.zeros(nx)
        xl = np.zeros(nx)
        xu = np.zeros(nx)
        for i in range(nx):
            var = problem.variable(i)
            v = problem.variable_view(i)
            if v.is_fixed():
                xl[i] = v.value()
                xu[i] = v.value()
                xi[i] = v.value()
            else:
                lb = v.lower_bound()
                if is_inf(lb):
                    lb = -mc.user_upper_bound
                    logger.warning(
                        run_id,
                        'Variable {} lower bound is infinite: setting to {}',
                        var.name,
                        lb,
                    )

                ub = v.upper_bound()
                if is_inf(ub):
                    ub = mc.user_upper_bound
                    logger.warning(
                        run_id,
                        'Variable {} upper bound is infinite: setting to {}',
                        var.name,
                        ub,
                    )

                xl[i] = lb
                xu[i] = ub

                use_starting_point = False
                starting_point = 0.0
                if v.has_starting_point():
                    starting_point = v.starting_point()
                    if not is_inf(starting_point):
                        use_starting_point = (
                            starting_point >= lb and
                            starting_point <= ub
                        )

                if use_starting_point:
                    xi[i] = starting_point
                else:
                    xi[i] = max(lb, min(ub, 0))

        return xi, xl, xu
Esempio n. 7
0
    def _before_root_node(self, problem, upper_bound):
        if self._user_model is None:
            raise RuntimeError("No user model. Did you call 'before_solve'?")
        obbt_upper_bound = None
        if upper_bound is not None and not is_inf(upper_bound):
            obbt_upper_bound = upper_bound

        model = self._user_model
        obbt_start_time = current_time()
        try:
            perform_obbt_on_model(
                model, problem, obbt_upper_bound,
                timelimit=self.solver.config['obbt_timelimit'],
                simplex_maxiter=self.solver.config['obbt_simplex_maxiter'],
            )
            self._bac_telemetry.increment_obbt_time(
                seconds_elapsed_since(obbt_start_time)
            )
        except TimeoutError:
            logger.info(0, 'OBBT timed out')
            self._bac_telemetry.increment_obbt_time(
                seconds_elapsed_since(obbt_start_time)
            )
            return

        except Exception as ex:
            logger.warning(0, 'Error performing OBBT: {}', ex)
            self._bac_telemetry.increment_obbt_time(
                seconds_elapsed_since(obbt_start_time)
            )
            raise
Esempio n. 8
0
def perform_fbbt(problem, maxiter, timelimit, objective_upper_bound=None,
                 branching_variable=None):
    """Perform FBBT on `problem` with the given `maxiter` and `timelimit`."""
    bounds = ExpressionDict(problem)
    bounds_tightener = BoundsTightener(
        FBBTStopCriterion(max_iter=maxiter, timelimit=timelimit),
    )

    for variable in problem.variables:
        lb = problem.lower_bound(variable)
        ub = problem.upper_bound(variable)
        bounds[variable] = Interval(lb, ub)

    # For all intents and purposes here an infinite upper bound is the same
    # as no upper bound.
    if objective_upper_bound is not None and is_inf(objective_upper_bound):
        objective_upper_bound = None

    if objective_upper_bound is not None:
        root_expr = problem.objective.root_expr
        expr_bounds = Interval(None, objective_upper_bound)
        if root_expr not in bounds:
            bounds[root_expr] = expr_bounds
        else:
            existing_bounds = bounds[root_expr]
            new_bounds = existing_bounds.intersect(expr_bounds)
            bounds[root_expr] = new_bounds

    def get_bound(b):
        def _f(expr):
            return b[expr]
        return _f

    def set_bound(b):
        def _f(expr, value):
            b[expr] = value
        return _f

    _initialize_bounds(problem, bounds, get_bound(bounds), set_bound(bounds))

    try:
        with timeout(timelimit, 'Timeout in FBBT'):
            try:
                bounds_tightener.tighten(
                    problem,
                    bounds,
                    branching_variable=branching_variable,
                    objective_changed=objective_upper_bound is not None,
                )
            except EmptyIntervalError:
                pass
    except TimeoutError:
        pass

    _manage_infinity_bounds(
        problem, bounds, get_bound(bounds), set_bound(bounds)
    )
    return bounds
Esempio n. 9
0
def least_reduced_variable(problem, root_problem):
    # If any variable is unbounded, branch at 0.0
    for v in problem.variables:
        if is_inf(problem.lower_bound(v)) and is_inf(problem.upper_bound(v)):
            return problem.variable_view(v)

    assert problem.num_variables == root_problem.num_variables
    r = range_ratio(problem, root_problem)
    # Could not compute range ratio, for example all bounded variables are fixed
    # Return the first variable to be unbounded.
    if r is None:
        for v in problem.variables:
            if is_inf(problem.lower_bound(v)) or is_inf(
                    problem.upper_bound(v)):
                return problem.variable_view(v)
        return None
    var_idx = np.argmax(r)
    return problem.variable_view(var_idx)
Esempio n. 10
0
    def _get_constraints(self, problem, problem_eval):
        constraints = []
        for i, constraint in enumerate(problem.constraints):
            lb = constraint.lower_bound
            ub = constraint.upper_bound

            if lb is None or is_inf(lb):
                constraints.append({
                    "type": "ineq",
                    "fun": problem_eval.eval_constraint,
                    "jac": problem_eval.eval_constraint_jacobian,
                    "args": [i, ub, True, True],
                })
            elif ub is None or is_inf(ub):
                constraints.append({
                    "type": "ineq",
                    "fun": problem_eval.eval_constraint,
                    "jac": problem_eval.eval_constraint_jacobian,
                    "args": [i, lb, True, False],
                })
            elif is_close(lb, ub, atol=mc.epsilon):
                constraints.append({
                    "type": "eq",
                    "fun": problem_eval.eval_constraint,
                    "jac": problem_eval.eval_constraint_jacobian,
                    "args": [i, lb, False, False],
                })
            else:
                # Constraint has both lower and upper bounds, but it's not
                # an equality constraint.
                constraints.append({
                    "type": "ineq",
                    "fun": problem_eval.eval_constraint,
                    "jac": problem_eval.eval_constraint_jacobian,
                    "args": [i, lb, True, False],
                })
                constraints.append({
                    "type": "ineq",
                    "fun": problem_eval.eval_constraint,
                    "jac": problem_eval.eval_constraint_jacobian,
                    "args": [i, ub, True, True],
                })

        return constraints
Esempio n. 11
0
    def _branch_on_var(self, var):
        lower_bound = var.lower_bound()
        domain = var.domain
        if is_inf(lower_bound):
            if domain.is_real():
                lower_bound = -self.user_upper_bound
            else:
                lower_bound = -self.user_integer_upper_bound

        upper_bound = var.upper_bound()
        if is_inf(upper_bound):
            if domain.is_real():
                upper_bound = self.user_upper_bound
            else:
                upper_bound = self.user_integer_upper_bound

        step = (upper_bound - lower_bound) / self.k
        points = step * (np.arange(self.k - 1) + 1.0) + lower_bound
        assert np.all(np.isfinite(points))
        return BranchingPoint(var, points.tolist())
Esempio n. 12
0
def _manage_infinity_bounds(problem, _bounds, get_bound, set_bound):
    """In some cases variables bounds are numbers that are bigger than
    mc.infinity. Change them back to None.
    """
    for variable in problem.variables:
        expr_bounds = get_bound(variable)
        lower_bound = expr_bounds.lower_bound
        upper_bound = expr_bounds.upper_bound

        if is_inf(lower_bound):
            new_lower_bound = None
        else:
            new_lower_bound = lower_bound

        if is_inf(upper_bound):
            new_upper_bound = None
        else:
            new_upper_bound = upper_bound

        set_bound(variable, Interval(new_lower_bound, new_upper_bound))
Esempio n. 13
0
def range_ratio(problem, root_problem):
    assert problem.num_variables == root_problem.num_variables
    lower_bounds = np.array(problem.lower_bounds)
    upper_bounds = np.array(problem.upper_bounds)
    root_lower_bounds = np.array(root_problem.lower_bounds)
    root_upper_bounds = np.array(root_problem.upper_bounds)
    denominator = np.abs(root_upper_bounds - root_lower_bounds) + mc.epsilon
    numerator = upper_bounds - lower_bounds

    numerator_mask = ~is_inf(numerator)
    denominator_mask = ~is_inf(denominator)
    finite_numerator = np.zeros_like(numerator)
    finite_denominator = np.zeros_like(denominator) + mc.epsilon
    finite_numerator[numerator_mask] = numerator[numerator_mask]
    finite_denominator[denominator_mask] = denominator[denominator_mask]

    # All bounded variables are fixed, range ratio is not possible to compute
    if is_close(np.sum(np.abs(finite_numerator)), 0.0, atol=mc.epsilon):
        return None
    return np.nan_to_num(finite_numerator / finite_denominator)
Esempio n. 14
0
def _compute_variable_starting_point_as_float(var, mc, value):
    # Use starting point if present
    if value is not None:
        return value
    # If var has both bounds, use midpoint
    lb = var.lb
    if is_inf(lb, mc):
        lb = None
    ub = var.ub
    if is_inf(ub, mc):
        ub = None
    if lb is not None and ub is not None:
        return lb + 0.5 * (ub - lb)
    # If unbounded, use 0
    if lb is None and lb is None:
        return 0.0
    # If no lower bound, use upper bound
    if lb is None:
        return ub
    # Otherwise, use lower bound
    return lb
    def has_converged(self, state):
        if self._relaxation_is_linear:
            return True

        if self._nlp_solution is None:
            return False

        if is_inf(state.lower_bound):
            return False

        return is_close(
            state.lower_bound,
            self._nlp_solution,
            rtol=self.convergence_relative_tol,
        )
Esempio n. 16
0
    def _compute_gamma(self, optimal_obj, value):
        # Unknown primal
        if value is None or is_inf(value):
            return 1.0

        # |opt| = |obj| = 0.0
        if is_close(np.abs(optimal_obj), 0.0, atol=mc.epsilon):
            if is_close(np.abs(value), 0.0, atol=mc.epsilon):
                return 0.0

        # opt * obj < 0
        if np.sign(optimal_obj) * np.sign(value) < 0:
            return 1.0

        num = np.abs(optimal_obj - value)
        den = max(np.abs(optimal_obj), np.abs(value))
        return num / den