def generate_solution(self, tree, alternative, tasks_to_complete,
                          task_duration, due_dates):
        """
        A method that creates a solution to the scheduling problem.

        Args:
            alternative - a list of unordered methods
        """
        sim = simulator.LightSimulator(tree)
        sim.init_disablements(alternative, tasks_to_complete)
        hard_constrained = list(sim.hardConstrainedTasks)

        sol_order = []
        base = []
        i = 0
        while len(sol_order) < len(alternative):
            enabled_tasks = sim.get_enabled_tasks(alternative)
            to_schedule = list(enabled_tasks - set(sol_order))
            # choose one of tasks
            indices = []
            for task in to_schedule:
                indices.append(self.tasks.index(task))
            pheromones = [self.pheromone[i][j] for j in indices]
            fitness = []
            # output = ""
            for j in range(len(to_schedule)):
                if due_dates[to_schedule[j]] > 0:
                    fitness.append(pheromones[j] /
                                   pow(due_dates[to_schedule[j]], 2))
                    # output += '\033[91m' + str(fitness[-1]) + '\033[0m '
                    # print(due_dates[to_schedule[j]])
                else:
                    fitness.append(pheromones[j] / pow(500, 5))
                    # output += str(fitness[-1]) + ' '
            # print(output)
            if max(fitness) == 0:
                normalized_fitness = [
                    1.0 / len(fitness) for _ in range(len(fitness))
                ]
            else:
                normalized_fitness = [x / sum(fitness) for x in fitness]

            sol_order.append(to_schedule[AntColonyOptimization.roulette_wheel(
                normalized_fitness)])
            sim.execute_task(sol_order[-1], tasks_to_complete)
            if sol_order[-1] in hard_constrained:
                base.append(i)
            i += 1

        solution = Solution(sol_order, base, task_duration)

        return solution
    def generate_solution(tree, alternative, tasks_to_complete, task_duration):
        """
        A method that creates a solution to the scheduling problem.

        Args:
            alternative - a list of unordered methods
        """
        sim = simulator.LightSimulator(tree)

        base, _, _ = SimulatedAnnealing.create_feasible_base_order(
            alternative, [], tasks_to_complete, sim)
        sim.setup()

        [methods, base_index
         ] = SimulatedAnnealing.create_solution_from_base(base, alternative)

        solution = Solution(methods, base_index, task_duration)

        return solution
    def init_population(self, tree, alternative, tasks_to_complete,
                        task_duration):
        """
        A method that creates initial population of feasible solutions to the scheduling problem.

        Args:
            alternative - a list of unordered methods
        """
        self.population = []

        self.initialDurationEV = {}
        for method in alternative:
            self.initialDurationEV[method] = task_duration[method]

        sim = simulator.LightSimulator(tree)

        while len(self.population) < self.populationSize:

            base, _, _ = self.create_feasible_base_order(
                alternative, [], tasks_to_complete, sim)
            sim.setup()

            [methods,
             base_index] = self.create_solution_from_base(base, alternative)

            chromosome = Chromosome(methods, base_index,
                                    self.initialDurationEV)
            chromosome.calc_id()

            if chromosome.ID in self.populationID:
                continue

            self.population.append(chromosome)
            self.populationID.append(chromosome.ID)

        return [None, False]
    def optimize(self, tree, alternative, task_duration, release_dates,
                 due_dates, no_iter):
        """
        A method that starts simulated annealing algorithm for generating schedule from unordered list of methods.

        :param tree: TaemsTree with hierarchical task structure
        :param alternative: an unordered list of actions to schedule
        :param task_duration: dictionary, key: action id, value: action duration
        :param release_dates: dictionary, key: action id, value: action due date
        :param due_dates: dictionary, key: action id, value: action release date
        :param no_iter: number of iterations of the algorithm
        :return: [(Solution) best found solution, (int) iteration at which the solution was found]
        """
        sim = simulator.LightSimulator(tree)
        t = deepcopy(alternative)
        # tasks.extend(nonLocalTasks)
        sim.execute_alternative(t, t)
        tasks_to_complete = sim.completedTasks

        start = time.time()

        # generate initial solution
        self.current_solution = SimulatedAnnealing.generate_solution(
            tree, alternative, tasks_to_complete, task_duration)
        self.current_solution.evaluate(due_dates, release_dates)
        self.best_solution = self.current_solution

        iteration = 0
        best_solution_iter = 0
        while iteration < no_iter:
            [neighborhood, total_tardiness
             ] = self.generate_neighborhood(self.current_solution, sim,
                                            tasks_to_complete, due_dates,
                                            release_dates)

            if random.random() < 0.6:
                best_index = total_tardiness.index(min(total_tardiness))
            else:
                best_index = SimulatedAnnealing.roulette_wheel(total_tardiness)

            # update current solution
            if self.current_solution.total_tardiness >= neighborhood[
                    best_index].total_tardiness:
                self.current_solution = neighborhood[best_index]
            else:
                if random.random() < exp(
                        -float(neighborhood[best_index].total_tardiness -
                               self.current_solution.total_tardiness) /
                        self.temperature):
                    self.current_solution = neighborhood[best_index]

            # update best solution
            if self.current_solution.total_tardiness < self.best_solution.total_tardiness:
                self.best_solution = self.current_solution
                best_solution_iter = iteration

            # update temperature
            self.temperature *= self.cooling_factor
            iteration += 1

        print(time.time() - start)
        print("best solution: " + str(self.best_solution.total_tardiness))
        self.best_solution.print_schedule()

        return [self.best_solution, best_solution_iter]
    def optimize(self, tree, alternative, task_duration, release_dates,
                 due_dates, no_iter):
        """
        A method that starts genetic algorithm for generating schedule from unordered list of methods.

        :param tree: TaemsTree with hierarchical task structure
        :param alternative: an unordered list of actions to schedule
        :param task_duration: dictionary, key: action id, value: action duration
        :param release_dates: dictionary, key: action id, value: action due date
        :param due_dates: dictionary, key: action id, value: action release date
        :param no_iter: number of iterations of the algorithm
        :return: [(Solution) best found solution, (int) iteration at which the solution was found]
        """
        sim = simulator.LightSimulator(tree)
        t = deepcopy(alternative)
        # tasks.extend(nonLocalTasks)
        sim.execute_alternative(t, t)
        tasks_to_complete = sim.completedTasks

        start = time.time()

        self.init_population(tree, alternative, tasks_to_complete,
                             task_duration)

        self.evaluate_population(due_dates, release_dates)

        iteration = 0
        best_solution_iter = 0
        previous_best = min(self.rating)

        while iteration < no_iter:

            self.best_solution = self.population[self.rating.index(
                min(self.rating))]
            if self.best_solution.total_tardiness < previous_best:
                best_solution_iter = iteration
                previous_best = self.best_solution.total_tardiness

            next_gen = []
            next_gen_id = []
            next_gen_rating = []

            self.promote_elite(next_gen, next_gen_id, next_gen_rating)
            self.eliminate_the_worst()

            self.create_new_generation(next_gen, next_gen_id, next_gen_rating,
                                       due_dates, release_dates, 0, sim,
                                       tasks_to_complete)

            self.population = [x for x in next_gen]
            self.populationID = [x for x in next_gen_id]
            self.rating = [x for x in next_gen_rating]

            iteration += 1

        print(time.time() - start)
        print("best solution: " + str(self.best_solution.total_tardiness) +
              " " + str(best_solution_iter))
        self.best_solution.print_schedule()

        return [self.best_solution, best_solution_iter]
    def optimize(self, tree, alternative, task_duration, release_dates,
                 due_dates, no_iter):
        """
        A method that starts ant colony optimization algorithm for generating schedule from unordered list of methods.

        :param tree: TaemsTree with hierarchical task structure
        :param alternative: an unordered list of actions to schedule
        :param task_duration: dictionary, key: action id, value: action duration
        :param release_dates: dictionary, key: action id, value: action due date
        :param due_dates: dictionary, key: action id, value: action release date
        :param no_iter: number of iterations of the algorithm
        :return: [(Solution) best found solution, (int) iteration at which the solution was found]
        """
        sim = simulator.LightSimulator(tree)
        t = deepcopy(alternative)
        # tasks.extend(nonLocalTasks)
        sim.execute_alternative(t, t)
        tasks_to_complete = sim.completedTasks

        start = time.time()

        tabu = TabuSearch(5)
        self.tasks = [x for x in alternative]
        self.pheromone = np.full((len(self.tasks), len(self.tasks)),
                                 self.init_pheromone)

        iteration = 0
        best_solution_iter = 0
        while iteration < no_iter:
            solutions = []
            ratings = []
            for i in range(self.ant_number):
                solution = self.generate_solution(tree, alternative,
                                                  tasks_to_complete,
                                                  task_duration, due_dates)
                solution.evaluate(due_dates, release_dates)

                [optimized,
                 _] = tabu.optimize(tree, alternative, task_duration,
                                    release_dates, due_dates, 10, False)

                solutions.append(optimized)
                ratings.append(optimized.total_tardiness)

            if self.best_solution is None:
                self.best_solution = solutions[ratings.index(min(ratings))]
                best_solution_iter = iteration

            elif min(ratings) < self.best_solution.total_tardiness:
                self.best_solution = solutions[ratings.index(min(ratings))]
                best_solution_iter = iteration

            self.evaporate_pheromones()
            for item in solutions:
                self.add_pheromone(item)
            iteration += 1

        print(time.time() - start)
        print("best solution: " + str(self.best_solution.total_tardiness))
        self.best_solution.print_schedule()

        return [self.best_solution, best_solution_iter]
    def optimize(self,
                 tree,
                 alternative,
                 task_duration,
                 release_dates,
                 due_dates,
                 no_iter,
                 generate_initial_sol=True):
        """
        A method that starts tabu search algorithm for generating schedule from unordered list of methods.

        :param tree: TaemsTree with hierarchical task structure
        :param alternative: an unordered list of actions to schedule
        :param task_duration: dictionary, key: action id, value: action duration
        :param release_dates: dictionary, key: action id, value: action due date
        :param due_dates: dictionary, key: action id, value: action release date
        :param no_iter: number of iterations of the algorithm
        :param generate_initial_sol: a flag which defines if initial solution is to be generated or just
                                        optimize the alternative
        :return: [(Solution) best found solution, (int) iteration at which the solution was found]
        """
        sim = simulator.LightSimulator(tree)
        t = deepcopy(alternative)
        # tasks.extend(nonLocalTasks)
        sim.execute_alternative(t, t)
        tasks_to_complete = sim.completedTasks

        start = time.time()

        # generate initial solution
        if not generate_initial_sol:
            self.current_solution = Solution(alternative, [], task_duration)
        else:
            self.current_solution = TabuSearch.generate_solution(
                tree, alternative, tasks_to_complete, task_duration)
        self.current_solution.evaluate(due_dates, release_dates)
        self.best_solution = self.current_solution

        iteration = 0
        best_solution_iter = 0
        while iteration < no_iter:
            [neighborhood, neighborhood_move, total_tardiness
             ] = self.generate_neighborhood(self.current_solution, sim,
                                            tasks_to_complete, due_dates,
                                            release_dates)

            # find best solution not in tabu
            while True:
                # select a solution from neighborhood
                if random.random() < 0.6:
                    best_index = total_tardiness.index(min(total_tardiness))
                else:
                    best_index = TabuSearch.roulette_wheel(total_tardiness)

                if neighborhood_move[best_index] in self.tabu_list:
                    del neighborhood[best_index]
                    del neighborhood_move[best_index]
                    del total_tardiness[best_index]
                else:
                    break

            # if no neighborhood can be generated, finish the procedure
            if len(neighborhood) == 0:
                break

            # update current solution and tabu list
            self.current_solution = neighborhood[best_index]
            self.tabu_list.append([
                neighborhood_move[best_index][1],
                neighborhood_move[best_index][0]
            ])
            if len(self.tabu_list) > self.tabu_size:
                del self.tabu_list[0]

            # update best solution
            if self.current_solution.total_tardiness < self.best_solution.total_tardiness:
                self.best_solution = self.current_solution
                best_solution_iter = iteration

            iteration += 1

        print(time.time() - start)
        print("best solution: " + str(self.best_solution.total_tardiness))
        self.best_solution.print_schedule()

        return [self.best_solution, best_solution_iter]