def create_solution(self) -> MulticastMultiObjectiveSolution:
        """ Creates a valid solution to the problem.
        :return: MulticastMultiObjectiveSolution. """

        return MulticastMultiObjectiveSolution(number_of_variables=self.number_of_variables, 
                                                number_of_objectives=self.number_of_objectives, 
                                                graph=self.G, 
                                                root_node=self.root_node, 
                                                destination_nodes=self.destination_nodes, 
                                                number_of_constraints=self.number_of_constraints
                                                )
    def execute(
        self, parents: List[MulticastMultiObjectiveSolution]
    ) -> List[MulticastMultiObjectiveSolution]:
        child_tree = self.crossover(parents[0], parents[1])

        child = MulticastMultiObjectiveSolution(
            graph=child_tree,
            number_of_variables=parents[0].number_of_variables,
            number_of_objectives=parents[0].number_of_objectives,
            root_node=parents[0].root_node,
            destination_nodes=parents[0].destination_nodes,
            number_of_constraints=parents[0].number_of_constraints,
            max_delay=parents[0].max_delay,
            alpha=parents[0].alpha,
            phi=parents[0].phi,
            build_tree=False)
        return [child]
 def evaluate(self, solution: MulticastMultiObjectiveSolution) -> MulticastMultiObjectiveSolution:
     
     solution.objectives[0] = self._f_cost(solution)
     solution.objectives[1] = self._f_delay_1(solution)
     solution.objectives[2] = self._f_delay_3(solution)
     return solution