def test_next(self):
        print(">> pylfit.semantics.SynchronousConstrained.next(feature_state, targets, rules)")

        # Unit test
        data = [ \
        ([0,0,0],[0,0,1]), \
        ([0,0,0],[1,0,0]), \
        ([1,0,0],[0,0,0]), \
        ([0,1,0],[1,0,1]), \
        ([0,0,1],[0,0,1]), \
        ([1,1,0],[1,0,0]), \
        ([1,0,1],[0,1,0]), \
        ([0,1,1],[1,0,1]), \
        ([1,1,1],[1,1,0])]
        feature_names=["p_t-1","q_t-1","r_t-1"]
        target_names=["p_t","q_t","r_t"]

        dataset = pylfit.preprocessing.transitions_dataset_from_array(data=data, feature_names=feature_names, target_names=target_names)

        model = CDMVLP(features=dataset.features, targets=dataset.targets)
        model.compile(algorithm="synchronizer")
        model.fit(dataset=dataset)

        feature_state = Algorithm.encode_state([0,0,0], model.features)
        self.assertEqual(set([tuple(s) for s in SynchronousConstrained.next(feature_state, model.targets, model.rules, model.constraints)]), set([(1,0,0), (0, 0, 1)]))
        feature_state = Algorithm.encode_state([1,1,1], model.features)
        self.assertEqual(set([tuple(s) for s in SynchronousConstrained.next(feature_state, model.targets, model.rules, model.constraints)]), set([(1,1,0)]))
        feature_state = Algorithm.encode_state([0,1,0], model.features)
        self.assertEqual(set([tuple(s) for s in SynchronousConstrained.next(feature_state, model.targets, model.rules, model.constraints)]), set([(1,0,1)]))

        # Random tests
        for i in range(self._nb_tests):

            # Apply CDMVLP correctly
            model = random_CDMVLP( \
            nb_features=random.randint(1,self._nb_features), \
            nb_targets=random.randint(1,self._nb_targets), \
            max_feature_values=self._nb_feature_values, \
            max_target_values=self._nb_target_values, \
            algorithm="synchronizer")

            feature_state = random.choice(model.feature_states())
            feature_state = Algorithm.encode_state(feature_state, model.features)

            target_states = SynchronousConstrained.next(feature_state, model.targets, model.rules, model.constraints)

            domains = [set() for var in model.targets]

            # Apply synchronous semantics
            candidates = Synchronous.next(feature_state, model.targets, model.rules)

            # Apply constraints
            expected = []
            for s in candidates:
                valid = True
                for c in model.constraints:
                    if c.matches(list(feature_state)+list(s)):
                        valid = False
                        #eprint(c, " matches ", feature_state, ", ", s)
                        break
                if valid:
                    # Decode state with domain values
                    expected.append(s)

            for s2 in target_states:
                self.assertTrue(s2 in expected)

            for s2 in expected:
                self.assertTrue(s2 in target_states)
    def _check_rules_and_predictions(self, dataset, expected_string_rules,
                                     expected_string_constraints):
        expected_string_rules = [
            s.strip() for s in expected_string_rules.strip().split("\n")
            if len(s) > 0
        ]
        expected_string_constraints = [
            s.strip() for s in expected_string_constraints.strip().split("\n")
            if len(s) > 0
        ]

        expected_rules = []
        for string_rule in expected_string_rules:
            expected_rules.append(
                Rule.from_string(string_rule, dataset.features,
                                 dataset.targets))

        expected_constraints = []
        for string_constraint in expected_string_constraints:
            expected_constraints.append(
                Rule.from_string(string_constraint, dataset.features,
                                 dataset.targets))

        #eprint(expected_rules)

        rules, constraints = Synchronizer.fit(dataset)

        #eprint(output)

        for r in expected_rules:
            if r not in rules:
                eprint("Missing rule: ", r)
            self.assertTrue(r in rules)

        for r in rules:
            if r not in expected_rules:
                eprint("Additional rule: ", r)
            self.assertTrue(r in expected_rules)

        for r in expected_constraints:
            if r not in constraints:
                eprint("Missing constraint: ", r)
            self.assertTrue(r in constraints)

        for r in constraints:
            if r not in expected_constraints:
                eprint("Additional constraint: ", r)
            self.assertTrue(r in constraints)

        model = CDMVLP(dataset.features, dataset.targets, rules, constraints)

        #model.compile("synchronizer")
        #model.summary()

        expected = set((tuple(s1), tuple(s2)) for s1, s2 in dataset.data)

        predicted = model.predict(model.feature_states())
        predicted = set(
            (tuple(s1), tuple(s2)) for (s1, S2) in predicted for s2 in S2)

        eprint()
        done = 0
        for s1, s2 in expected:
            done += 1
            eprint("\rChecking transitions ", done, "/", len(expected), end='')
            self.assertTrue((s1, s2) in predicted)

        done = 0
        for s1, s2 in predicted:
            done += 1
            eprint("\rChecking transitions ",
                   done,
                   "/",
                   len(predicted),
                   end='')
            self.assertTrue((s1, s2) in expected)