Exemplo n.º 1
0
 def update_stats_for_method_pair(self, destroy: Method, repair: Method, sol: Solution, res: Result, obj_old: TObj,
                                  t_destroy: float, t_repair: float):
     """Update statistics, incumbent and check termination condition after having performed a destroy+repair."""
     if __debug__ and self.own_settings.mh_checkit:
         sol.check()
     ms_destroy = self.method_stats[destroy.name]
     ms_destroy.applications += 1
     ms_destroy.netto_time += t_destroy
     ms_destroy.brutto_time += t_destroy
     ms_repair = self.method_stats[repair.name]
     ms_repair.applications += 1
     ms_repair.netto_time += t_repair
     ms_repair.brutto_time += t_repair
     obj_new = sol.obj()
     if sol.is_better_obj(sol.obj(), obj_old):
         ms_destroy.successes += 1
         ms_destroy.obj_gain += obj_new - obj_old
         ms_repair.successes += 1
         ms_repair.obj_gain += obj_new - obj_old
     self.iteration += 1
     new_incumbent = self.update_incumbent(sol, time.process_time() - self.time_start)
     terminate = self.check_termination()
     self.log_iteration(destroy.name+'+'+repair.name, obj_old, sol, new_incumbent,
                        terminate, res.log_info)
     if terminate:
         self.run_time = time.process_time() - self.time_start
         res.terminate = True
Exemplo n.º 2
0
 def metropolis_criterion(self, sol_new: Solution,
                          sol_current: Solution) -> bool:
     """Apply Metropolis criterion as acceptance decision, return True when sol_new should be accepted."""
     if sol_new.is_better(sol_current):
         return True
     return np.random.random_sample() <= exp(
         -abs(sol_new.obj() - sol_current.obj()) / self.temperature)
Exemplo n.º 3
0
    def perform_method(self,
                       method: Method,
                       sol: Solution,
                       delayed_success=False) -> Result:
        """Perform method on given solution and returns Results object.

        Also updates incumbent, iteration and the method's statistics in method_stats.
        Furthermore checks the termination condition and eventually sets terminate in the returned Results object.

        :param method: method to be performed
        :param sol: solution to which the method is applied
        :param delayed_success: if set the success is not immediately determined and updated but at some later
                call of delayed_success_update()
        :returns: Results object
        """
        res = Result()
        obj_old = sol.obj()
        ##### logging for visualisation
        if self.step_logger.hasHandlers():
            sol_str, inc_str = f'{sol}'.replace(
                '\n', ' '), f'{self.incumbent}'.replace('\n', ' ')
            step_info = f'START\nSOL: {sol_str}\nOBJ: {obj_old}\nM: {method.name}\n' + \
                f'PAR: {method.par}\nINC: {inc_str}\nBEST: {self.incumbent.obj()}'
            self.step_logger.info(step_info)
        #################
        t_start = time.process_time()
        method.func(sol, method.par, res)
        t_end = time.process_time()
        if __debug__ and self.own_settings.mh_checkit:
            sol.check()
        ms = self.method_stats[method.name]
        ms.applications += 1
        ms.netto_time += t_end - t_start
        obj_new = sol.obj()
        if not delayed_success:
            ms.brutto_time += t_end - t_start
            if sol.is_better_obj(sol.obj(), obj_old):
                ms.successes += 1
                ms.obj_gain += obj_new - obj_old
        self.iteration += 1
        new_incumbent = self.update_incumbent(sol, t_end - self.time_start)
        ##### logging for visualisation
        if self.step_logger.hasHandlers():
            sol_str, inc_str = f'{sol}'.replace(
                '\n', ' '), f'{self.incumbent}'.replace('\n', ' ')
            step_info = f'END\nSOL: {sol_str}\nOBJ: {sol.obj()}\nM: {method.name}\n' + \
                f'PAR: {method.par}\nINC: {inc_str}\nBEST: {self.incumbent.obj()}\nBETTER: {new_incumbent}'
            self.step_logger.info(step_info)
        #################
        terminate = self.check_termination()
        self.log_iteration(method.name, obj_old, sol, new_incumbent, terminate,
                           res.log_info)
        if terminate:
            self.run_time = time.process_time() - self.time_start
            res.terminate = True
        return res
Exemplo n.º 4
0
    def delayed_success_update(self, method: Method, obj_old: TObj, t_start: float, sol: Solution):
        """Update an earlier performed method's success information in method_stats.

        :param method: earlier performed method
        :param obj_old: objective value of solution with which to compare to determine success
        :param t_start: time when the application of method had started
        :param sol: current solution considered the final result of the method
        """
        t_end = time.process_time()
        ms = self.method_stats[method.name]
        ms.brutto_time += t_end - t_start
        obj_new = sol.obj()
        if sol.is_better_obj(sol.obj(), obj_old):
            ms.successes += 1
            ms.obj_gain += obj_new - obj_old
Exemplo n.º 5
0
 def gvns(self, sol: Solution):
     """Perform general variable neighborhood search (GVNS) to given solution."""
     sol2 = sol.copy()
     if self.vnd(sol2) or not self.meths_sh:
         return
     use_vnd = bool(self.meths_li)
     while True:
         for m in self.next_method(self.meths_sh, repeat=True):
             t_start = time.process_time()
             res = self.perform_method(m, sol2, delayed_success=use_vnd)
             terminate = res.terminate
             if not terminate and use_vnd:
                 terminate = self.vnd(sol2)
             self.delayed_success_update(m, sol.obj(), t_start, sol2)
             if sol2.is_better(sol):
                 sol.copy_from(sol2)
                 if terminate or res.terminate:
                     return
                 break
             else:
                 if terminate or res.terminate:
                     return
                 sol2.copy_from(sol)
         else:
             break
Exemplo n.º 6
0
    def perform_methods(self, methods: List[Method], sol: Solution) -> Result:
        """Performs all methods on given solution and returns Results object.

        Also updates incumbent, iteration and the method's statistics in method_stats.
        Furthermore checks the termination condition and eventually sets terminate in the returned Results object.

        :param methods: list of methods to perform
        :param sol: solution to which the method is applied
        :returns: Results object
        """
        res = Result()
        obj_old = sol.obj()
        method_name = ""
        for method in methods:
            if method_name != "":
                method_name += "+"
            method_name += method.name

            method.func(sol, method.par, res)
            if res.terminate:
                break
        t_end = time.process_time()

        self.iteration += 1
        new_incumbent = self.update_incumbent(sol, t_end - self.time_start)
        terminate = self.check_termination()
        self.log_iteration(method_name, obj_old, sol, new_incumbent, terminate, res.log_info)
        if terminate:
            self.run_time = time.process_time() - self.time_start
            res.terminate = True

        return res
Exemplo n.º 7
0
    def ts(self, sol: Solution):

        while True:
            # use of multiple different methods for restricted neighborhood search is possible,
            # but usually only one is used
            for m in self.next_method(self.meths_rli, repeat=True):
                sol_old = sol.copy()

                def ts_iteration(sol: Solution, _par, result):

                    for ta in self.tabu_list.tabu_list:
                        self.step_logger.info(f'TA: {ta}')

                    m.func(sol, m.par, None, self.tabu_list, self.incumbent)

                ts_method = Method(m.name, ts_iteration, m.par)

                t_start = time.process_time()
                res = self.perform_method(ts_method, sol, delayed_success=True)
                self.update_tabu_list(sol, sol_old)
                self.delayed_success_update(m, sol.obj(), t_start, sol_old)

                for ta in self.tabu_list.tabu_list:
                    self.step_logger.info(f'TA: {ta}')

                if res.terminate:
                    return
Exemplo n.º 8
0
    def __init__(self,
                 sol: Solution,
                 meths_ch: List[Method],
                 meths_destroy: List[Method],
                 meths_repair: List[Method],
                 own_settings: dict = None,
                 consider_initial_sol=False):
        """Initialization.

        :param sol: solution to be improved
        :param meths_ch: list of construction heuristic methods
        :param meths_destroy: list of destroy methods
        :param meths_repair: list of repair methods
        :param own_settings: optional dictionary with specific settings
        :param consider_initial_sol: if true consider sol as valid solution that should be improved upon; otherwise
            sol is considered just a possibly uninitialized of invalid solution template
        """
        super().__init__(sol, meths_ch + meths_destroy + meths_repair,
                         own_settings, consider_initial_sol)
        self.meths_ch = meths_ch
        assert meths_destroy and meths_repair
        self.meths_destroy = meths_destroy
        self.meths_repair = meths_repair
        self.score_data = {
            m.name: ScoreData()
            for m in chain(self.meths_destroy, self.meths_repair)
        }
        self.temperature = sol.obj(
        ) * self.own_settings.mh_alns_init_temp_factor + 0.000000001
        self.next_segment = 0
Exemplo n.º 9
0
    def perform_method(self,
                       method: Method,
                       sol: Solution,
                       delayed_success=False) -> Result:
        """Perform method on given solution and returns Results object.

        Also updates incumbent, iteration and the method's statistics in method_stats.
        Furthermore checks the termination condition and eventually sets terminate in the returned Results object.

        :param method: method to be performed
        :param sol: solution to which the method is applied
        :param delayed_success: if set the success is not immediately determined and updated but at some later
                call of delayed_success_update()
        :returns: Results object
        """
        res = Result()
        obj_old = sol.obj()
        t_start = time.process_time()
        method.func(sol, method.par, res)
        t_end = time.process_time()
        if __debug__ and self.own_settings.mh_checkit:
            sol.check()
        ms = self.method_stats[method.name]
        ms.applications += 1
        ms.netto_time += t_end - t_start
        obj_new = sol.obj()
        if not delayed_success:
            ms.brutto_time += t_end - t_start
            if sol.is_better_obj(sol.obj(), obj_old):
                ms.successes += 1
                ms.obj_gain += obj_new - obj_old
        self.iteration += 1
        new_incumbent = self.update_incumbent(sol, t_end - self.time_start)
        terminate = self.check_termination()
        self.log_iteration(method.name, obj_old, sol, new_incumbent, terminate,
                           res.log_info)
        if terminate:
            self.run_time = time.process_time() - self.time_start
            res.terminate = True
        return res
Exemplo n.º 10
0
    def perform_method_pair(self, destroy: Method, repair: Method, sol: Solution) -> Result:
        """Performs a destroy/repair method pair on given solution and returns Results object.

        Also updates incumbent, iteration and the method's statistics in method_stats.
        Furthermore checks the termination condition and eventually sets terminate in the returned Results object.

        :param destroy: destroy destroy method to be performed
        :param repair: repair destroy method to be performed
        :param sol: solution to which the method is applied
        :returns: Results object
        """
        res = Result()
        obj_old = sol.obj()
        t_start = time.process_time()
        destroy.func(sol, destroy.par, res)
        t_destroyed = time.process_time()
        repair.func(sol, repair.par, res)
        t_end = time.process_time()
        self.update_stats_for_method_pair(destroy, repair, sol, res, obj_old,
                                          t_destroyed - t_start, t_end - t_destroyed)
        return res