def save_population_operation(self):
        population = RulePopulation(self.STARTING_SYMBOL)
        for rule_model in self.population_editor.rules:
            if rule_model.is_terminal:
                parent, terminal_word = Rule.from_human_friendly_representation(
                    population.symbol_shift(),
                    population.starting_symbol,
                    population.universal_symbol,
                    rule_model.parent,
                    rule_model.left_child,
                    rule_model.right_child
                )
                terminal_symbol = self.translator.word_to_symbol(terminal_word)

                population.add_rule(Rule(parent, terminal_symbol), self.randomizer)
            else:
                parent, left, right = Rule.from_human_friendly_representation(
                    population.symbol_shift(),
                    population.starting_symbol,
                    population.universal_symbol,
                    rule_model.parent,
                    rule_model.left_child,
                    rule_model.right_child
                )

                population.add_rule(Rule(parent, left, right), self.randomizer)

        name = os.path.basename(self.population_editor.population_path).split('.')[0]
        path = os.path.dirname(self.population_editor.population_path)
        self.simulation_executor.save_population_data(population, path, name)
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.sut = RulePopulation('S')

        self.rules = [
            Rule('A', 'B', 'C'),
            Rule('D', 'B', 'C'),
            Rule('A', 'J', 'C'),
            Rule('A', 'B', 'J')
        ]

        self.randomizer_mock = create_autospec(Randomizer)
    def create_rules(self, rules):
        rule_population = RulePopulation(Symbol('S'), universal_symbol=Symbol('U'))
        for rule in rules:
            rule_population.add_rule(rule, self.randomizer)

        return rule_population
 def _random_symbol_id(self, configuration):
     return self.randomizer.randint(
         RulePopulation.symbol_shift(),
         RulePopulation.symbol_shift() + configuration.rule.max_non_terminal_symbols)
 def create_rule_population(self):
     self.starting_symbol = Symbol('S')
     self.rule_population = RulePopulation(self.starting_symbol)
class TestEvolution(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.randomizer = Randomizer(Random())

        selector_configuration = [
            EvolutionRandomSelectorConfiguration.create(),
            EvolutionRouletteSelectorConfiguration.create()
        ]
        self.configuration = EvolutionConfiguration.create(
            selector_configuration, inversion_chance=0, mutation_chance=0, crossover_chance=0)
        self.create_rule_population()
        self.create_grammar_statistics()
        self.create_rule_adding()

        self.sut = EvolutionService(self.randomizer)

        self.rules = [
            Rule(Symbol('S'), Symbol('NP'), Symbol('VP')),
            Rule(Symbol('VP'), Symbol('VP'), Symbol('PP')),
            Rule(Symbol('VP'), Symbol('V'), Symbol('NP')),
            TerminalRule(Symbol('VP'), Symbol('eats')),
            Rule(Symbol('PP'), Symbol('P'), Symbol('NP')),
            Rule(Symbol('NP'), Symbol('Det'), Symbol('N')),
            TerminalRule(Symbol('NP'), Symbol('she')), TerminalRule(Symbol('V'), Symbol('eats')),
            TerminalRule(Symbol('P'), Symbol('with')), TerminalRule(Symbol('N'), Symbol('fish')),
            TerminalRule(Symbol('N'), Symbol('fork')), TerminalRule(Symbol('Det'), Symbol('a'))
        ]

    def create_rule_population(self):
        self.starting_symbol = Symbol('S')
        self.rule_population = RulePopulation(self.starting_symbol)

    def create_grammar_statistics(self):
        configuration = ClassicalStatisticsConfiguration.default()
        configuration.base_fitness = 5
        configuration.classical_fitness_weight = 1
        configuration.fertility_weight = 1
        configuration.positive_weight = 1
        configuration.negative_weight = 1

        self.grammar_statistics = GrammarStatistics(
            configuration, self.randomizer, ClassicRuleStatistics(),
            ClassicFitness())

    def create_rule_adding(self):
        configuration = AddingRulesConfiguration.create(
            crowding_factor=2,
            crowding_size=3,
            elitism_size=2,
            max_non_terminal_rules=19
        )

        adding_strategies = [SimpleAddingRuleStrategy(),
                             AddingRuleWithCrowdingStrategy(),
                             AddingRuleWithElitismStrategy()]

        self.rule_adding = AddingRuleSupervisor(self.randomizer, configuration, adding_strategies)

    def simulate_induction_part_work(self, rules):
        for rule in rules:
            self.rule_adding.add_rule(rule, self.rule_population, self.grammar_statistics)

        for rule in rules:
            rule_usage_info = ClassicRuleUsageInfo(True, 1)
            positive_usages = self.randomizer.randint(1, 4)
            for _ in range(positive_usages):
                self.grammar_statistics.on_rule_usage(rule, rule_usage_info)

            rule_usage_info.positive_sentence = False
            self.grammar_statistics.on_rule_usage(rule, rule_usage_info)

            self.grammar_statistics.fitness.get(self.grammar_statistics, rule)

    def get_symbols_from_rules(self, rules):
        return {y for y in chain.from_iterable(
            (x.parent, x.left_child, x.right_child) for x in rules)}

    def assert_contains_rules(self, rules, rules_pool):
        for rule in rules:
            assert_that(rules_pool, has_item(rule))

    def count_rules_that_has_changed(self, old):
        changed_rules = 0
        for rule in self.rule_population.get_all_non_terminal_rules():
            changed_rules += 1 if rule not in old else 0
        return changed_rules

    def test_given_no_operator_used_rule_population_should_remain_unchanged(self):
        # Given:
        self.simulate_induction_part_work(self.rules)

        old_population = copy.deepcopy(self.rule_population)

        # When:
        self.sut.run_genetic_algorithm(self.grammar_statistics, self.rule_population,
                                       self.rule_adding, self.configuration)

        # Then:
        self.assert_contains_rules(self.rule_population.get_all_non_terminal_rules(),
                                   list(old_population.get_all_non_terminal_rules()))

        old_symbols = self.get_symbols_from_rules(old_population.get_all_non_terminal_rules())
        new_symbols = self.get_symbols_from_rules(self.rule_population.get_all_non_terminal_rules())
        assert_that(old_symbols, has_items(*new_symbols))

    def promote_those_rules_to_elite(self, rules):
        for rule in rules:
            rule_usage_info = ClassicRuleUsageInfo(True, 1)
            positive_usages = 5
            for _ in range(positive_usages):
                self.grammar_statistics.on_rule_usage(rule, rule_usage_info)

    def test_given_high_operator_usage_rule_population_should_expect_some_major_change(self):
        # Given:
        self.configuration.operators.inversion.chance = 1
        self.configuration.operators.mutation.chance = 1
        self.configuration.operators.crossover.chance = 1

        self.simulate_induction_part_work(self.rules)
        self.promote_those_rules_to_elite(self.rules[0:2])

        elite = list(self.rules[0:2])

        old_population = copy.deepcopy(self.rule_population)

        # When:
        self.sut.run_genetic_algorithm(self.grammar_statistics, self.rule_population,
                                       self.rule_adding, self.configuration)

        # Then:
        self.assert_contains_rules(elite, list(old_population.get_all_non_terminal_rules()))

        assert_that(self.count_rules_that_has_changed(old_population.get_all_non_terminal_rules()),
                    is_(greater_than_or_equal_to(2)))

        old_symbols = self.get_symbols_from_rules(old_population.get_all_non_terminal_rules())
        new_symbols = self.get_symbols_from_rules(self.rule_population.get_all_non_terminal_rules())
        assert_that(old_symbols, not_(has_items(*new_symbols)))
class TestRulePopulation(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.sut = RulePopulation('S')

        self.rules = [
            Rule('A', 'B', 'C'),
            Rule('D', 'B', 'C'),
            Rule('A', 'J', 'C'),
            Rule('A', 'B', 'J')
        ]

        self.randomizer_mock = create_autospec(Randomizer)

    def add_rules(self):
        for rule in self.rules:
            self.sut.add_rule(rule, self.randomizer_mock)

        assert_that(self.sut.get_all_non_terminal_rules(), contains_inanyorder(*self.rules))

    def test_adding_rule_should_result_in_storing_it(self):
        # Given:
        self.add_rules()

        # When/Then:
        assert_that(self.sut.get_rules_by_right(('B', 'C')),
                    only_contains(self.rules[0], self.rules[1]))
        assert_that(self.sut.get_rules_by_right(('B', 'B')), is_(empty()))
        assert_that(self.sut.get_rules_by_right(('A', 'B')), is_(empty()))
        assert_that(calling(self.sut.get_rules_by_right).with_args(('A',)),
                    raises(RulePopulationAccessViolationError))
        assert_that(calling(self.sut.get_rules_by_right).with_args(('A', 'B', 'J')),
                    raises(RulePopulationAccessViolationError))

    def test_should_be_able_to_add_terminal_rule(self):
        rule_a = TerminalRule(Symbol('A'), Symbol('a'))
        rule_b = TerminalRule(Symbol('B'), Symbol('a'))
        self.sut.add_rule(rule_a, self.randomizer_mock)
        self.sut.add_rule(rule_b, self.randomizer_mock)

        assert_that(self.sut._rules_by_right, is_(empty()))
        assert_that(self.sut.get_terminal_rules(Symbol('a')), only_contains(rule_a, rule_b))
        assert_that(self.sut.get_terminal_rules(Symbol('b')), is_(empty()))
        assert_that(self.sut.get_terminal_rules(), only_contains(rule_a, rule_b))

    def test_universal_symbol_should_work_properly(self):
        # Given:
        rule_a = TerminalRule(self.sut.universal_symbol, Symbol('a'))
        rule_b = TerminalRule(self.sut.universal_symbol, Symbol('b'))

        # When:
        self.sut.add_rule(rule_a, self.randomizer_mock)
        self.sut.add_rule(rule_b, self.randomizer_mock)

        # Then:
        assert_that(self.sut._rules_by_right, is_(empty()))
        assert_that(self.sut.get_terminal_rules(Symbol('a')), only_contains(rule_a))
        assert_that(self.sut.get_terminal_rules(Symbol('b')), only_contains(rule_b))
        assert_that(self.sut.get_terminal_rules(), only_contains(rule_a, rule_b))

    def test_should_be_able_to_obtain_random_population(self):
        # Given:
        self.add_rules()

        self.randomizer_mock.sample.return_value = [Rule('A', 'J', 'C'), Rule('D', 'B', 'C')]

        # When:
        rules = self.sut.get_random_rules(self.randomizer_mock, False, 2)

        # Then:
        assert_that(rules, only_contains(Rule('D', 'B', 'C'), Rule('A', 'J', 'C')))
        assert_that(self.randomizer_mock.sample.called)

    def test_should_be_able_to_remove_a_rule(self):
        # Given:
        self.add_rules()
        assert_that(
            self.sut.get_rules_by_right((self.rules[0].left_child, self.rules[0].right_child)),
            only_contains(self.rules[0], self.rules[1])
        )

        # When:
        self.sut.remove_rule(self.rules[0])

        # Then:
        assert_that(
            self.sut.get_rules_by_right((self.rules[0].left_child, self.rules[0].right_child)),
            only_contains(self.rules[1])
        )

    def test_should_be_able_to_obtain_random_population_matching_filter(self):
        # Given:
        self.add_rules()

        filter = lambda x: x.right_child == 'C'

        self.randomizer_mock.sample.return_value = [Rule('A', 'J', 'C'), Rule('D', 'B', 'C')]

        # When:
        rules = self.sut.get_random_rules_matching_filter(self.randomizer_mock, False, 2, filter)

        # Then:
        assert_that(rules, only_contains(Rule('D', 'B', 'C'), Rule('A', 'J', 'C')))
        assert_that(self.randomizer_mock.sample.call_count, is_(equal_to(1)))

    def test_should_know_if_rule_already_exists(self):
        # Given:
        self.add_rules()
        not_added_rule = Rule('J', 'J', 'J')

        # When:
        for rule in self.rules:
            assert_that(self.sut.has_rule(rule))
        assert_that(not_(self.sut.has_rule(not_added_rule)))