# Path: lcs/agents/xncs/Configuration.py
# class Configuration(xcs.Configuration):
# 
#     def __init__(self,
#                  number_of_actions: int,  # theta_mna it is actually smart to make it equal to number of actions
#                  lmc: int = 100,
#                  lem: float = 1,
#                  classifier_wildcard: str = '#',
#                  max_population: int = 200,  # n
#                  learning_rate: float = 0.1,  # beta
#                  alpha: float = 0.1,
#                  epsilon_0: float = 10,
#                  v: int = 5,
#                  gamma: float = 0.71,
#                  ga_threshold: int = 25,
#                  chi: float = 0.5,
#                  mutation_chance: float = 0.01,  # mu
#                  deletion_threshold: int = 20,  # theta_del
#                  delta: float = 0.1,
#                  subsumption_threshold: int = 20,  # theta_sub
#                  covering_wildcard_chance: float = 0.33,  # population wildcard
#                  initial_prediction: float = float(np.finfo(np.float32).tiny),  # p_i
#                  initial_error: float = float(np.finfo(np.float32).tiny),  # epsilon_i
#                  initial_fitness: float = float(np.finfo(np.float32).tiny),  # f_i
#                  epsilon: float = 0.5,  # p_exp, exploration probability
#                  do_ga_subsumption: bool = False,
#                  do_action_set_subsumption: bool = False,
#                  metrics_trial_frequency: int = 5,
#                  user_metrics_collector_fcn: Callable = None
#                  ) -> None:
#         self.lmc = lmc
#         self.lem = lem
#         self.classifier_wildcard = classifier_wildcard
#         self.max_population = max_population
#         self.learning_rate = learning_rate
#         self.alpha = alpha
#         self.epsilon_0 = epsilon_0
#         self.v = v
#         self.gamma = gamma
#         self.ga_threshold = ga_threshold
#         self.chi = chi
#         self.mutation_chance = mutation_chance
#         self.deletion_threshold = deletion_threshold
#         self.delta = delta
#         self.subsumption_threshold = subsumption_threshold
#         self.covering_wildcard_chance = covering_wildcard_chance
#         self.initial_prediction = initial_prediction
#         self.initial_error = initial_error
#         self.initial_fitness = initial_fitness
#         self.epsilon = epsilon  # p_exp, probability of exploration
#         self.number_of_actions = number_of_actions
#         self.do_GA_subsumption = do_ga_subsumption
#         self.do_action_set_subsumption = do_action_set_subsumption
# 
#         self.metrics_trial_frequency = metrics_trial_frequency
#         self.user_metrics_collector_fcn = user_metrics_collector_fcn
# 
# Path: lcs/agents/xncs/Classifier.py
# class Classifier(xcs.Classifier):
#     def __init__(self,
#                  cfg: Optional[Configuration] = None,
#                  condition: Union[Condition, str, None] = None,
#                  action: Optional[int] = None,
#                  time_stamp: int = None,
#                  effect:  Union[Effect, str, None] = None) -> None:
#         self.effect = effect
#         super().__init__(cfg, condition, action, time_stamp)
# 
#     def __eq__(self, other):
#         if other.action == self.action \
#            and other.condition == self.condition:
#             if other.effect is None and self.effect is None:
#                 return True
#             if other.effect is None:
#                 return False
#             if self.effect is None:
#                 return False
#             if other.effect == self.effect:
#                 return True
#         return False
# 
# Path: lcs/agents/xncs/Effect.py
# class Effect(ImmutableSequence):
# 
#     def __init__(self, observation):
#         super().__init__(observation)
# 
#     def subsumes(self, other) -> bool:
#         for ci, oi in zip(self, other):
#             if ci != self.WILDCARD and oi != self.WILDCARD and ci != oi:
#                 return False
#         return True


from copy import copy
from lcs.agents.xncs import Configuration, Classifier, Effect



class Backpropagation:

    def __init__(self, cfg: Configuration):
        self.classifiers_for_update = []
        self.update_vectors = []
        self.cfg = cfg
        self.update_cycles = 0

    def insert_into_bp(self,
                       classifier: Classifier,
                       update_vector: Effect):
        self.classifiers_for_update.append(classifier)
        self.update_vectors.append(update_vector)

    def update_bp(self):
        for cl, uv in zip(self.classifiers_for_update, self.update_vectors):
            if cl.effect is None:

# Path: lcs/agents/xncs/Configuration.py
# class Configuration(xcs.Configuration):
# 
#     def __init__(self,
#                  number_of_actions: int,  # theta_mna it is actually smart to make it equal to number of actions
#                  lmc: int = 100,
#                  lem: float = 1,
#                  classifier_wildcard: str = '#',
#                  max_population: int = 200,  # n
#                  learning_rate: float = 0.1,  # beta
#                  alpha: float = 0.1,
#                  epsilon_0: float = 10,
#                  v: int = 5,
#                  gamma: float = 0.71,
#                  ga_threshold: int = 25,
#                  chi: float = 0.5,
#                  mutation_chance: float = 0.01,  # mu
#                  deletion_threshold: int = 20,  # theta_del
#                  delta: float = 0.1,
#                  subsumption_threshold: int = 20,  # theta_sub
#                  covering_wildcard_chance: float = 0.33,  # population wildcard
#                  initial_prediction: float = float(np.finfo(np.float32).tiny),  # p_i
#                  initial_error: float = float(np.finfo(np.float32).tiny),  # epsilon_i
#                  initial_fitness: float = float(np.finfo(np.float32).tiny),  # f_i
#                  epsilon: float = 0.5,  # p_exp, exploration probability
#                  do_ga_subsumption: bool = False,
#                  do_action_set_subsumption: bool = False,
#                  metrics_trial_frequency: int = 5,
#                  user_metrics_collector_fcn: Callable = None
#                  ) -> None:
#         self.lmc = lmc
#         self.lem = lem
#         self.classifier_wildcard = classifier_wildcard
#         self.max_population = max_population
#         self.learning_rate = learning_rate
#         self.alpha = alpha
#         self.epsilon_0 = epsilon_0
#         self.v = v
#         self.gamma = gamma
#         self.ga_threshold = ga_threshold
#         self.chi = chi
#         self.mutation_chance = mutation_chance
#         self.deletion_threshold = deletion_threshold
#         self.delta = delta
#         self.subsumption_threshold = subsumption_threshold
#         self.covering_wildcard_chance = covering_wildcard_chance
#         self.initial_prediction = initial_prediction
#         self.initial_error = initial_error
#         self.initial_fitness = initial_fitness
#         self.epsilon = epsilon  # p_exp, probability of exploration
#         self.number_of_actions = number_of_actions
#         self.do_GA_subsumption = do_ga_subsumption
#         self.do_action_set_subsumption = do_action_set_subsumption
# 
#         self.metrics_trial_frequency = metrics_trial_frequency
#         self.user_metrics_collector_fcn = user_metrics_collector_fcn
# 
# Path: lcs/agents/xncs/Classifier.py
# class Classifier(xcs.Classifier):
#     def __init__(self,
#                  cfg: Optional[Configuration] = None,
#                  condition: Union[Condition, str, None] = None,
#                  action: Optional[int] = None,
#                  time_stamp: int = None,
#                  effect:  Union[Effect, str, None] = None) -> None:
#         self.effect = effect
#         super().__init__(cfg, condition, action, time_stamp)
# 
#     def __eq__(self, other):
#         if other.action == self.action \
#            and other.condition == self.condition:
#             if other.effect is None and self.effect is None:
#                 return True
#             if other.effect is None:
#                 return False
#             if self.effect is None:
#                 return False
#             if other.effect == self.effect:
#                 return True
#         return False
# 
# Path: lcs/agents/xncs/Effect.py
# class Effect(ImmutableSequence):
# 
#     def __init__(self, observation):
#         super().__init__(observation)
# 
#     def subsumes(self, other) -> bool:
#         for ci, oi in zip(self, other):
#             if ci != self.WILDCARD and oi != self.WILDCARD and ci != oi:
#                 return False
#         return True


# Path: lcs/agents/xncs/Backpropagation.py
from copy import copy
from lcs.agents.xncs import Configuration, Classifier, Effect




class Backpropagation:

    def __init__(self, cfg: Configuration):
        self.classifiers_for_update = []
        self.update_vectors = []
        self.cfg = cfg
        self.update_cycles = 0

    def insert_into_bp(self,
                       classifier: Classifier,
                       update_vector: Effect):
        self.classifiers_for_update.append(classifier)
        self.update_vectors.append(update_vector)

    def update_bp(self):
        for cl, uv in zip(self.classifiers_for_update, self.update_vectors):
            if cl.effect is None:

                cl.effect = copy(Effect(uv))