def test_crossover(self, cfg): # given parent = Classifier( condition=Condition( [UBR(1, 1), UBR(1, 1), UBR(1, 1)], cfg), effect=Effect( [UBR(1, 1), UBR(1, 1), UBR(1, 1)], cfg), cfg=cfg) donor = Classifier( condition=Condition( [UBR(2, 2), UBR(2, 2), UBR(2, 2)], cfg), effect=Effect( [UBR(2, 2), UBR(2, 2), UBR(2, 2)], cfg), cfg=cfg) # when np.random.seed(12345) # left: 3, right: 6 crossover(parent, donor) # then assert parent.condition == \ Condition([UBR(1, 1), UBR(1, 2), UBR(2, 2)], cfg) assert parent.effect == \ Effect([UBR(1, 1), UBR(1, 2), UBR(2, 2)], cfg) assert donor.condition == \ Condition([UBR(2, 2), UBR(2, 1), UBR(1, 1)], cfg) assert donor.effect == \ Effect([UBR(2, 2), UBR(2, 1), UBR(1, 1)], cfg)
def test_should_subsume_effect(self, _effect1, _effect2, _result, cfg): # given effect1 = Effect(_effect1, cfg=cfg) effect2 = Effect(_effect2, cfg=cfg) # then assert effect1.subsumes(effect2) == _result
def test_should_get_maximum_fitness(self, cfg): # given # anticipate change - low fitness cl1 = Classifier(effect=Effect([UBR(0, 1), UBR(0, 3)], cfg), quality=0.3, reward=1, cfg=cfg) # do not anticipate change - high fitness cl2 = Classifier(effect=Effect([UBR(0, 15), UBR(0, 15)], cfg), quality=0.5, reward=1, cfg=cfg) # anticipate change - medium fitness cl3 = Classifier(effect=Effect([UBR(0, 14), UBR(0, 15)], cfg), quality=0.4, reward=1, cfg=cfg) population = ClassifierList(*[cl1, cl2, cl3]) # when mf = population.get_maximum_fitness() # then assert mf == cl3.fitness
def crossover(parent: Classifier, donor: Classifier): assert parent.cfg.classifier_length == donor.cfg.classifier_length # flatten parent and donor perception strings p_cond_flat = _flatten(parent.condition) d_cond_flat = _flatten(donor.condition) p_effect_flat = _flatten(parent.effect) d_effect_flat = _flatten(donor.effect) # select crossing points left, right = sorted( np.random.choice(range(0, len(p_cond_flat) + 1), 2, replace=False)) assert left < right # extract chromosomes p_cond_chromosome = p_cond_flat[left:right] d_cond_chromosome = d_cond_flat[left:right] p_effect_chromosome = p_effect_flat[left:right] d_effect_chromosome = d_effect_flat[left:right] # Flip everything p_cond_flat[left:right] = d_cond_chromosome d_cond_flat[left:right] = p_cond_chromosome p_effect_flat[left:right] = d_effect_chromosome d_effect_flat[left:right] = p_effect_chromosome # Rebuild proper perception strings parent.condition = Condition(_unflatten(p_cond_flat), cfg=parent.cfg) donor.condition = Condition(_unflatten(d_cond_flat), cfg=donor.cfg) parent.effect = Effect(_unflatten(p_effect_flat), cfg=parent.cfg) donor.effect = Effect(_unflatten(d_effect_flat), cfg=parent.cfg)
def test_should_specialize(self, _p0, _p1, _effect, is_specializable, cfg): # given p0 = Perception(_p0, oktypes=(float, )) p1 = Perception(_p1, oktypes=(float, )) effect = Effect(_effect, cfg=cfg) # then assert effect.is_specializable(p0, p1) is is_specializable
def test_should_detect_identical_classifier(self, cfg): cl_1 = Classifier(condition=Condition([UBR(0, 1), UBR(0, 2)], cfg=cfg), action=1, effect=Effect([UBR(2, 3), UBR(4, 5)], cfg=cfg), cfg=cfg) cl_2 = Classifier(condition=Condition([UBR(0, 1), UBR(0, 2)], cfg=cfg), action=1, effect=Effect([UBR(2, 3), UBR(4, 5)], cfg=cfg), cfg=cfg) assert cl_1 == cl_2
def test_aggressive_mutation(self, _cond, _effect, cfg): # given condition = Condition(_cond, cfg) effect = Effect(_effect, cfg) cfg.encoder = RealValueEncoder(16) # more precise encoder cfg.mutation_noise = 0.5 # strong noise mutation range mu = 1.0 # mutate every attribute cl = Classifier(condition=deepcopy(condition), effect=deepcopy(effect), cfg=cfg) # when mutate(cl, mu) # then range_min, range_max = cfg.encoder.range for idx, (c, e) in enumerate(zip(cl.condition, cl.effect)): # assert that we have new locus if condition[idx] != cfg.classifier_wildcard: assert condition[idx] != c if effect[idx] != cfg.classifier_wildcard: assert effect[idx] != e # assert if condition values are in ranges assert c.lower_bound >= range_min assert c.upper_bound <= range_max # assert if effect values are in ranges assert e.lower_bound >= range_min assert e.upper_bound <= range_max
def test_should_create_new_classifier_with_covering( self, _p0, _p1, _child_cond, _child_effect, cfg): # given cfg.cover_noise = 0.0 p0 = Perception(_p0, oktypes=(float, )) p1 = Perception(_p1, oktypes=(float, )) action = random.randint(0, cfg.number_of_possible_actions) time = random.randint(0, 100) # when new_cl = cover(p0, action, p1, time, cfg) # then assert new_cl is not None assert new_cl.condition == Condition(_child_cond, cfg) assert new_cl.action == action assert new_cl.effect == Effect(_child_effect, cfg) assert new_cl.q == .5 assert new_cl.r == 0 assert new_cl.ir == 0 assert new_cl.tav == 0 assert new_cl.tga == time assert new_cl.talp == time # assert new_cl.num == 1 assert new_cl.exp == 0
def test_should_create_copy(self, cfg): # given operation_time = random.randint(0, 100) condition = Condition( [self._random_ubr(), self._random_ubr()], cfg=cfg) action = random.randint(0, 2) effect = Effect([self._random_ubr(), self._random_ubr()], cfg=cfg) cl = Classifier(condition, action, effect, quality=random.random(), reward=random.random(), intermediate_reward=random.random(), cfg=cfg) # when copied_cl = Classifier.copy_from(cl, operation_time) # then assert cl is not copied_cl assert cl.condition == copied_cl.condition assert cl.condition is not copied_cl.condition assert cl.action == copied_cl.action assert cl.effect == copied_cl.effect assert cl.effect is not copied_cl.effect assert copied_cl.is_marked() is False assert cl.r == copied_cl.r assert cl.q == copied_cl.q assert operation_time == copied_cl.tga assert operation_time == copied_cl.talp
def test_should_create_pass_through_effect(self, cfg): # when effect = Effect.pass_through(cfg) # then assert len(effect) == cfg.classifier_length for allele in effect: assert allele == cfg.classifier_wildcard
def test_should_find_similar(self): # given cfg = Configuration(3, 2, encoder=RealValueEncoder(2)) cl1 = Classifier( condition=Condition([UBR(0, 0), UBR(0, 3), UBR(0, 3)], cfg=cfg), action=0, effect=Effect([UBR(0, 3), UBR(0, 3), UBR(0, 3)], cfg=cfg), cfg=cfg ) cl2 = Classifier( condition=Condition([UBR(0, 0), UBR(0, 3), UBR(0, 3)], cfg=cfg), action=0, effect=Effect([UBR(0, 3), UBR(0, 3), UBR(0, 3)], cfg=cfg), cfg=cfg ) # then assert cl1 == cl2
def test_should_return_zero_max_fitness(self, cfg): # given classifiers that does not anticipate change cl1 = Classifier(effect=Effect([UBR(0, 15), UBR(0, 15)], cfg), quality=0.5, reward=1, cfg=cfg) cl2 = Classifier(effect=Effect([UBR(0, 15), UBR(0, 15)], cfg), quality=0.7, reward=1, cfg=cfg) population = ClassifierList(*[cl1, cl2]) # when mf = population.get_maximum_fitness() # then assert mf == 0.0
def test_should_count_specified_unchanging_attributes( self, _condition, _effect, _sua, cfg): # given cl = Classifier(condition=Condition(_condition, cfg), effect=Effect(_effect, cfg), cfg=cfg) # then assert len(cl.specified_unchanging_attributes) == _sua
def test_should_initialize_without_arguments(self, cfg): # when c = Classifier(cfg=cfg) # then assert c.condition == Condition.generic(cfg=cfg) assert c.action is None assert c.effect == Effect.pass_through(cfg=cfg) assert c.exp == 1 assert c.talp is None assert c.tav == 0.0
def test_should_detect_subsumption(self, _e1, _e2, _exp1, _marked, _reliable, _more_general, _condition_matching, _result, mocker, cfg): # given cl1 = Classifier(effect=Effect(_e1, cfg), experience=_exp1, cfg=cfg) cl2 = Classifier(effect=Effect(_e2, cfg), cfg=cfg) # when mocker.patch.object(cl1, "is_reliable") mocker.patch.object(cl1, "is_marked") mocker.patch.object(cl1, "is_more_general") mocker.patch.object(cl1.condition, "does_match_condition") cl1.is_reliable.return_value = _reliable cl1.is_marked.return_value = _marked cl1.is_more_general.return_value = _more_general cl1.condition.does_match_condition.return_value = _condition_matching # then assert cl1.does_subsume(cl2) == _result
def test_should_handle_unexpected_case_3(self, cfg): # given p0 = Perception([.5, .5], oktypes=(float, )) p1 = Perception([.5, .8], oktypes=(float, )) # Second effect attribute is specializable effect = Effect([UBR(0, 15), UBR(10, 14)], cfg=cfg) quality = 0.4 time = random.randint(0, 1000) cl = Classifier(effect=effect, quality=quality, cfg=cfg) # when child = unexpected_case(cl, p0, p1, time) # then assert cl.q < quality assert cl.is_marked() is True assert child is not None assert child.is_marked() is False assert child.q == .5 assert child.condition == Condition([UBR(0, 15), UBR(0, 15)], cfg=cfg) assert child.effect == Effect([UBR(0, 15), UBR(10, 14)], cfg=cfg)
def test_should_generalize_randomly_unchanging_condition_attribute( self, _condition, _effect, _soa_before, _soa_after, cfg): # given condition = Condition(_condition, cfg) effect = Effect(_effect, cfg) cl = Classifier(condition=condition, effect=effect, cfg=cfg) assert len(cl.specified_unchanging_attributes) == _soa_before # when cl.generalize_unchanging_condition_attribute() # then assert (len(cl.specified_unchanging_attributes)) == _soa_after
def test_should_handle_unexpected_case_1(self, cfg): # given p0 = Perception([.5, .5], oktypes=(float, )) p1 = Perception([.5, .5], oktypes=(float, )) # Effect is all pass-through. Can be specialized. effect = Effect([UBR(0, 15), UBR(0, 15)], cfg=cfg) quality = .4 time = random.randint(0, 1000) cl = Classifier(effect=effect, quality=quality, cfg=cfg) # when child = unexpected_case(cl, p0, p1, time) # then assert cl.q < quality assert cl.is_marked() is True assert child assert child.q == .5 assert child.talp == time # There is no change in perception so the child condition # and effect should stay the same. assert child.condition == Condition([UBR(0, 15), UBR(0, 15)], cfg=cfg) assert child.effect == Effect([UBR(0, 15), UBR(0, 15)], cfg=cfg)
def test_should_handle_unexpected_case_2(self, cfg): # given p0 = Perception([.5, .5], oktypes=(float, )) p1 = Perception([.5, .5], oktypes=(float, )) # Effect is not specializable effect = Effect([UBR(0, 15), UBR(2, 4)], cfg=cfg) quality = random.random() time = random.randint(0, 1000) cl = Classifier(effect=effect, quality=quality, cfg=cfg) # when child = unexpected_case(cl, p0, p1, time) # then assert cl.q < quality assert cl.is_marked() is True # We cannot generate child from non specializable parent assert child is None
def test_disabled_mutation(self, _cond, _effect, cfg): # given condition = Condition(_cond, cfg) effect = Effect(_effect, cfg) cl = Classifier(condition=deepcopy(condition), effect=deepcopy(effect), cfg=cfg) mu = 0.0 # when mutate(cl, mu) # then for idx, (c, e) in enumerate(zip(cl.condition, cl.effect)): assert c.lower_bound == condition[idx].lower_bound assert c.upper_bound == condition[idx].upper_bound assert e.lower_bound == effect[idx].lower_bound assert e.upper_bound == effect[idx].upper_bound
def test_should_visualize(self, _effect, _result, cfg): assert repr(Effect(_effect, cfg=cfg)) == _result
def test_should_flatten_effect(self, _effect, _result, cfg): assert _flatten(Effect(_effect, cfg=cfg)) == _result
def test_should_detect_change(self, _e, _result, cfg): assert Effect(_e, cfg).specify_change == _result