Source code for discrete_optimization.generic_tools.mutations.mutation_portfolio

#  Copyright (c) 2022 AIRBUS and its affiliates.
#  This source code is licensed under the MIT license found in the
#  LICENSE file in the root directory of this source tree.
import logging
from collections.abc import Container
from typing import Union

import numpy as np

from discrete_optimization.generic_tools.do_mutation import LocalMove, Mutation
from discrete_optimization.generic_tools.do_problem import Problem, Solution
from discrete_optimization.generic_tools.mutations.mutation_catalog import (
    get_available_mutations,
)

logger = logging.getLogger(__name__)


[docs] class PortfolioMutation(Mutation): """Mutations portfolio. Randomly choose between available mutations. """ def __init__( self, problem: Problem, list_mutations: list[Mutation], weight_mutations: Union[list[float], np.ndarray], **kwargs, ): super().__init__(problem=problem, **kwargs) self.list_mutations = list_mutations self.weight_mutation = weight_mutations if isinstance(self.weight_mutation, list): self.weight_mutation = np.array(self.weight_mutation) self.weight_mutation = self.weight_mutation / np.sum(self.weight_mutation) self.index_np = np.array(range(len(self.list_mutations)), dtype=np.int_)
[docs] def choose_a_mutation(self) -> int: return int(np.random.choice(self.index_np, size=1, p=self.weight_mutation)[0])
[docs] def mutate(self, solution: Solution) -> tuple[Solution, LocalMove]: choice = self.choose_a_mutation() logger.debug(f"mutate with {self.list_mutations[choice]}") return self.list_mutations[choice].mutate(solution)
[docs] def mutate_and_compute_obj( self, solution: Solution ) -> tuple[Solution, LocalMove, dict[str, float]]: choice = self.choose_a_mutation() return self.list_mutations[choice].mutate_and_compute_obj(solution)
def __repr__(self): return f"{type(self).__name__}(list_mutations='{self.list_mutations}')"
[docs] def create_mutations_portfolio_from_problem( problem: Problem, selected_mutations: Container[type[Mutation]] | None = None, selected_attributes: Container[str] | None = None, ) -> PortfolioMutation: """Create a mutation mixing all mutations available in catalog for the solution attributes.""" # available mutations for the encoding attributes specified by the problem/solution list_mutations = get_available_mutations(problem) def _filter( mutation_type: type[Mutation], attribute_name: str, selected_mutations: Container[type[Mutation]] | None = None, selected_attributes: Container[str] | None = None, ) -> bool: return ( selected_attributes is None or attribute_name in selected_attributes ) and (selected_mutations is None or mutation_type in selected_mutations) list_built_mutations = [ mutation_cls.build(problem=problem, attribute=attribute_name, **mutation_kwargs) for mutation_cls, mutation_kwargs, attribute_name in list_mutations if _filter( mutation_cls, attribute_name, selected_mutations, selected_attributes ) ] # create a mixed mutation that sample one of the given mutations return PortfolioMutation( problem=problem, list_mutations=list_built_mutations, weight_mutations=np.ones((len(list_built_mutations))), )