コード例 #1
0
ファイル: test_tree.py プロジェクト: michaelbynum/galini
 def test_update_root(self):
     tree = BabTree(MockNodeStorage(create_problem()),
                    KSectionBranchingStrategy(), MockSelectionStrategy())
     sol = create_solution(5.0, 10.0)
     tree.update_root(sol)
     assert np.isclose(5.0, tree.lower_bound)
     assert np.isclose(10.0, tree.upper_bound)
コード例 #2
0
ファイル: test_tree.py プロジェクト: michaelbynum/galini
def tree():
    """Create a BaBTree like:

            0
       +----+----+
       |    |    |
       1    2    3
               +-+-+
               |   |
               4   5

    with coordinates:

     0 : [0]
     1 : [0, 0]
     2 : [0, 1]
     3 : [0, 2]
     4 : [0, 2, 0]
     5 : [0, 2, 1]

    """
    problem = create_problem()
    storage = MockNodeStorage(problem)
    t = BabTree(storage, KSectionBranchingStrategy(), MockSelectionStrategy())
    t.update_root(create_solution(-30.0, 0.0))
    root = t.root
    bp = BranchingPoint(problem.variable(0), [0.0, 0.5])
    root.branch_at_point(bp)

    c = root.children[2]
    bp = BranchingPoint(problem.variable(1), [0.0])
    c.branch_at_point(bp)

    return t
コード例 #3
0
ファイル: test_strategy.py プロジェクト: michaelbynum/galini
 def test_ksection(self, problem, solution):
     ksection_strat = KSectionBranchingStrategy(7)
     tree = BabTree(
         MockNodeStorage(problem),
         ksection_strat,
         FakeSelectionStrategy(),
     )
     node = tree.root
     for i in range(5):
         node.update(create_solution(0.0, 1.0))
         children, _ = node.branch(ksection_strat)
         assert len(children) == 7
         for child in children:
             assert child.variable.idx == i
         node = children[0]
コード例 #4
0
    def _bab_loop(self, problem, run_id, **kwargs):
        known_optimal_objective = kwargs.get('known_optimal_objective', None)
        if known_optimal_objective is not None:
            if not problem.objective.original_sense.is_minimization():
                known_optimal_objective = -known_optimal_objective

        self._bac_telemetry.start_timing(
            known_optimal_objective,
            elapsed_time(),
        )

        branching_strategy = self._algo.branching_strategy
        node_selection_strategy = self._algo.node_selection_strategy

        bab_iteration = 0

        root_node_storage = RootNodeStorage(problem)
        tree = BabTree(root_node_storage, branching_strategy,
                       node_selection_strategy)
        self._tree = tree

        logger.info(run_id, 'Finding initial feasible solution')
        initial_solution = self._algo.find_initial_solution(
            run_id, problem, tree, tree.root)
        if initial_solution is not None:
            tree.add_initial_solution(initial_solution)
            self._bac_telemetry.update_at_end_of_iteration(
                tree, elapsed_time())
            self._telemetry.log_at_end_of_iteration(run_id, bab_iteration)
            if self._algo.should_terminate(tree.state):
                return

        logger.info(run_id, 'Solving root problem')
        root_solution = self._algo.solve_problem_at_root(
            run_id, problem, tree, tree.root)
        tree.update_root(root_solution)

        self._bac_telemetry.update_at_end_of_iteration(
            tree, elapsed_time(), update_nodes_visited=False)
        self._telemetry.log_at_end_of_iteration(run_id, bab_iteration)
        bab_iteration += 1

        logger.info(run_id, 'Root problem solved, tree state {}', tree.state)
        logger.log_add_bab_node(
            run_id,
            coordinate=[0],
            lower_bound=root_solution.lower_bound,
            upper_bound=root_solution.upper_bound,
        )

        while not self._algo.should_terminate(tree.state):
            logger.info(run_id, 'Tree state at beginning of iteration: {}',
                        tree.state)
            if not tree.has_nodes():
                logger.info(run_id, 'No more nodes to visit.')
                break

            current_node = tree.next_node()
            if current_node.parent is None:
                # This is the root node.
                node_children, branching_point = tree.branch_at_node(
                    current_node)
                logger.info(run_id, 'Branched at point {}', branching_point)
                continue
            else:
                current_node_problem = current_node.storage.problem
                var_view = \
                    current_node_problem.variable_view(current_node.variable)

            logger.info(
                run_id,
                'Visiting node {}: parent state={}',
                current_node.coordinate,
                current_node.parent.state,
            )

            node_can_not_improve_solution = is_close(
                current_node.parent.lower_bound,
                tree.upper_bound,
                atol=self._algo.tolerance,
                rtol=self._algo.relative_tolerance,
            ) or current_node.parent.lower_bound > tree.upper_bound

            if node_can_not_improve_solution:
                logger.info(
                    run_id,
                    "Fathom node because it won't improve bound: node.lower_bound={}, tree.upper_bound={}",
                    current_node.parent.lower_bound,
                    tree.upper_bound,
                )
                logger.log_prune_bab_node(run_id, current_node.coordinate)
                tree.fathom_node(current_node, update_nodes_visited=True)
                self._bac_telemetry.update_at_end_of_iteration(
                    tree, elapsed_time())
                self._telemetry.log_at_end_of_iteration(run_id, bab_iteration)
                bab_iteration += 1
                continue

            solution = self._algo.solve_problem_at_node(
                run_id, current_node.storage.problem, tree, current_node)

            tree.update_node(current_node, solution)
            logger.log_add_bab_node(
                run_id,
                coordinate=current_node.coordinate,
                lower_bound=solution.lower_bound,
                upper_bound=solution.upper_bound,
            )
            current_node_converged = is_close(
                solution.lower_bound,
                solution.upper_bound,
                atol=self._algo.tolerance,
                rtol=self._algo.relative_tolerance,
            )

            if not current_node_converged and solution.upper_bound_solution is not None:
                node_children, branching_point = tree.branch_at_node(
                    current_node)
                logger.info(run_id, 'Branched at point {}', branching_point)
            else:
                # We won't explore this part of the tree anymore.
                # Add to fathomed nodes.
                logger.info(
                    run_id,
                    'Fathom node {}, converged? {}, upper_bound_solution {}',
                    current_node.coordinate, current_node_converged,
                    solution.upper_bound_solution)
                logger.log_prune_bab_node(run_id, current_node.coordinate)
                tree.fathom_node(current_node, update_nodes_visited=False)

            self._log_problem_information_at_node(run_id,
                                                  current_node.storage.problem,
                                                  solution, current_node)
            logger.info(run_id, 'New tree state at {}: {}',
                        current_node.coordinate, tree.state)
            logger.update_variable(run_id, 'z_l', tree.nodes_visited,
                                   tree.lower_bound)
            logger.update_variable(run_id, 'z_u', tree.nodes_visited,
                                   tree.upper_bound)
            logger.info(
                run_id,
                'Child {} has solutions: LB={} UB={}',
                current_node.coordinate,
                solution.lower_bound_solution,
                solution.upper_bound_solution,
            )
            self._bac_telemetry.update_at_end_of_iteration(
                tree, elapsed_time())
            self._telemetry.log_at_end_of_iteration(run_id, bab_iteration)
            bab_iteration += 1

        logger.info(run_id, 'Branch & Bound Finished: {}', tree.state)
        logger.info(run_id, 'Branch & Bound Converged?: {}',
                    self._algo._has_converged(tree.state))
        logger.info(run_id, 'Branch & Bound Timeout?: {}',
                    self._algo._timeout())
        logger.info(run_id, 'Branch & Bound Node Limit Exceeded?: {}',
                    self._algo._node_limit_exceeded(tree.state))
コード例 #5
0
ファイル: test_tree.py プロジェクト: michaelbynum/galini
    def test_update_with_new_lower_bound(self):
        tree = BabTree(MockNodeStorage(create_problem()),
                       KSectionBranchingStrategy(), MockSelectionStrategy())
        sol = create_solution(5.0, 10.0)
        tree.update_root(sol)

        root_children, _ = tree.branch_at_node(tree.root)
        a, b = root_children
        tree.update_node(a, create_solution(7.0, 11.0))

        tree.branch_at_node(a)

        assert np.isclose(5.0, tree.lower_bound)
        assert np.isclose(10.0, tree.upper_bound)

        tree.update_node(b, create_solution(6.0, 10.0))
        children, _ = tree.branch_at_node(b)

        assert np.isclose(6.0, tree.lower_bound)
        assert np.isclose(10.0, tree.upper_bound)

        a, b = children
        tree.update_node(a, create_solution(7.0, 10.0))
        tree.branch_at_node(a)

        assert np.isclose(6.0, tree.lower_bound)
        assert np.isclose(10.0, tree.upper_bound)
コード例 #6
0
ファイル: algorithm.py プロジェクト: cog-imperial/galini
    def _bab_loop(self, model, **kwargs):
        known_optimal_objective = kwargs.get('known_optimal_objective', None)
        if known_optimal_objective is not None:
            if not model._objective.is_originally_minimizing:
                known_optimal_objective = -known_optimal_objective

        branching_strategy = self.branching_strategy
        node_selection_strategy = self.node_selection_strategy

        root_node_storage = self.init_node_storage(model)
        tree = BabTree(root_node_storage, branching_strategy,
                       node_selection_strategy)
        self._tree = tree

        self.logger.info('Finding initial feasible solution')

        with self._telemetry.timespan(
                'branch_and_bound.find_initial_solution'):
            initial_solution = self.find_initial_solution(
                model, tree, tree.root)

        prev_elapsed_time = None

        if initial_solution is not None:
            tree.add_initial_solution(initial_solution, self.galini.mc)
            if self.should_terminate(tree.state):
                delta_t, prev_elapsed_time = _compute_delta_t(
                    self.galini, prev_elapsed_time)
                update_at_end_of_iteration(self.galini, tree, delta_t)
                return True

        self.logger.info('Solving root problem')
        with self._telemetry.timespan(
                'branch_and_bound.solve_problem_at_root'):
            root_solution = self.solve_problem_at_root(tree, tree.root)
        tree.update_root(root_solution)

        delta_t, prev_elapsed_time = _compute_delta_t(self.galini,
                                                      prev_elapsed_time)
        update_at_end_of_iteration(self.galini, tree, delta_t)

        self.logger.info('Root problem solved, tree state {}', tree.state)
        self.logger.info('Root problem solved, root solution {}',
                         root_solution)
        self.logger.log_add_bab_node(
            coordinate=[0],
            lower_bound=root_solution.lower_bound,
            upper_bound=root_solution.upper_bound,
        )

        if not root_solution.lower_bound_success:
            if not root_solution.upper_bound_success:
                return False

        mc = self.galini.mc

        while not self.should_terminate(tree.state):
            self.logger.info('Tree state at beginning of iteration: {}',
                             tree.state)
            if not tree.has_nodes():
                self.logger.info('No more nodes to visit.')
                break

            current_node = tree.next_node()
            if current_node.parent is None:
                # This is the root node.
                node_children, branching_point = tree.branch_at_node(
                    current_node, mc)
                self.logger.info('Branched at point {}', branching_point)
                continue

            self.logger.info(
                'Visiting node {}: parent state={}',
                current_node.coordinate,
                current_node.parent.state,
            )

            node_can_not_improve_solution = is_close(
                current_node.parent.lower_bound,
                tree.upper_bound,
                atol=self.bab_config['absolute_gap'],
                rtol=self.bab_config['relative_gap'],
            ) or current_node.parent.lower_bound > tree.upper_bound

            if node_can_not_improve_solution:
                self.logger.info(
                    "Fathom node because it won't improve bound: node.lower_bound={}, tree.upper_bound={}",
                    current_node.parent.lower_bound,
                    tree.upper_bound,
                )
                self.logger.log_prune_bab_node(current_node.coordinate)
                tree.fathom_node(current_node, update_nodes_visited=True)

                delta_t, prev_elapsed_time = _compute_delta_t(
                    self.galini, prev_elapsed_time)
                update_at_end_of_iteration(self.galini, tree, delta_t)

                continue

            with self._telemetry.timespan(
                    'branch_and_bound.solve_problem_at_node'):
                solution = self.solve_problem_at_node(tree, current_node)
            assert solution is not None

            tree.update_node(current_node, solution)
            self.logger.info('Node {} solution: {}', current_node.coordinate,
                             solution)
            self.logger.log_add_bab_node(
                coordinate=current_node.coordinate,
                lower_bound=solution.lower_bound,
                upper_bound=solution.upper_bound,
            )
            current_node_converged = is_close(
                solution.lower_bound,
                tree.upper_bound,
                atol=self.bab_config['absolute_gap'],
                rtol=self.bab_config['relative_gap'],
            ) or solution.lower_bound > tree.upper_bound

            node_relaxation_is_feasible_or_unbounded = (
                solution.lower_bound_solution is not None
                and (solution.lower_bound_solution.status.is_success()
                     or solution.lower_bound_solution.status.is_unbounded()))

            if not current_node_converged and node_relaxation_is_feasible_or_unbounded:
                node_children, branching_point = tree.branch_at_node(
                    current_node, mc)
                self.logger.info('Branched at point {}', branching_point)
            else:
                # We won't explore this part of the tree anymore.
                # Add to fathomed nodes.
                self.logger.info(
                    'Fathom node {}, lower_bound_solution: {}, tree upper bound: {}',
                    current_node.coordinate, solution.lower_bound,
                    tree.upper_bound)
                self.logger.log_prune_bab_node(current_node.coordinate)
                tree.fathom_node(current_node, update_nodes_visited=False)

            self.logger.info('New tree state at {}: {}',
                             current_node.coordinate, tree.state)
            self.logger.update_variable('z_l', tree.nodes_visited,
                                        tree.lower_bound)
            self.logger.update_variable('z_u', tree.nodes_visited,
                                        tree.upper_bound)
            self.logger.info(
                'Child {} has solutions: LB={} UB={}',
                current_node.coordinate,
                solution.lower_bound_solution,
                solution.upper_bound_solution,
            )

            delta_t, prev_elapsed_time = _compute_delta_t(
                self.galini, prev_elapsed_time)
            update_at_end_of_iteration(self.galini, tree, delta_t)

        self.logger.info('Branch & Bound Finished: {}', tree.state)
        self.logger.info('Branch & Bound Converged?: {}',
                         self.has_converged(tree.state))
        self.logger.info('Branch & Bound Timeout?: {}',
                         self.galini.timelimit.timeout())
        self.logger.info('Branch & Bound Node Limit Exceeded?: {}',
                         self.node_limit_exceeded(tree.state))

        return True