Skip to content

Commit 8c298c9

Browse files
author
Edi Muškardin
authored
Merge pull request #94 from taburg/rpni-moore-initial-output-fix
Fix RPNI possibly discarding initial output of Moore machines
2 parents 0a95a23 + 707b1b6 commit 8c298c9

File tree

2 files changed

+34
-6
lines changed

2 files changed

+34
-6
lines changed

aalpy/learning_algs/deterministic_passive/rpni_helper_functions.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ def check_sequence(root_node, seq, automaton_type):
6060
Checks whether each sequence in the dataset is valid in the current automaton.
6161
"""
6262
curr_node = root_node
63+
# Check initial output for Moore machines and the like
64+
if automaton_type != 'mealy' and len(seq) != 0:
65+
if seq[0] is not None and seq[0] != root_node.output:
66+
return False
67+
seq = seq[1:]
6368
for i, o in seq:
6469
if automaton_type == 'mealy':
6570
if i not in curr_node.output or o is not None and curr_node.output[i] != o:
@@ -117,7 +122,7 @@ def _get_leaf_nodes(node):
117122
leaf_nodes = get_leaf_nodes(root_node)
118123
paths = []
119124
for node in leaf_nodes:
120-
seq = []
125+
seq = [] if automaton_type == 'mealy' else [root_node.output]
121126
curr_node = root_node
122127
for i in node.prefix:
123128
curr_node = curr_node.children[i]

tests/test_deterministic_passive.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,21 @@ def prove_equivalence(self, learned_automaton):
2626

2727
return correct_automaton == learned_automaton # bisimilar
2828

29-
def generate_data(self, ground_truth, depth=5):
29+
def generate_data(self, ground_truth, depth=5, step=1):
3030
data = []
3131
if isinstance(ground_truth, aalpy.automata.Dfa) or isinstance(ground_truth, aalpy.automata.MooreMachine):
3232
data.append(((), ground_truth.initial_state.output))
3333

3434
alphabet = ground_truth.get_input_alphabet()
35-
for level in range(1, depth + 1):
35+
for level in range(1, depth + 1, step):
3636
for seq in product(alphabet, repeat=level):
3737
ground_truth.reset_to_initial()
3838
outputs = ground_truth.execute_sequence(ground_truth.initial_state, seq)
3939
data.append((seq, outputs[-1]))
4040

4141
return data
4242

43-
# TODO incomplete: skips input completeness options
4443
def test_all_configuration_combinations(self):
45-
46-
4744
automata_type = {Dfa: 'dfa', MooreMachine: 'moore', MealyMachine: 'mealy'}
4845
algorithms = ['gsm', 'classic']
4946

@@ -68,3 +65,29 @@ def test_all_configuration_combinations(self):
6865

6966
assert True
7067

68+
def test_all_configuration_combinations_input_incomplete_data(self):
69+
automata_type = {Dfa: 'dfa', MooreMachine: 'moore', MealyMachine: 'mealy'}
70+
algorithms = ['gsm', 'classic']
71+
72+
for automata in correct_automata:
73+
correct_automaton = correct_automata[automata]
74+
alphabet = correct_automaton.get_input_alphabet()
75+
data = self.generate_data(correct_automaton, depth=3, step=2)
76+
if automata_type[automata] == 'moore':
77+
data += [(('a', 'a', 'a', 'a'), 1), (('b', 'b', 'b', 'b'), 2), (('c', 'c', 'c', 'c'), 3)]
78+
for algorithm in algorithms:
79+
learned_model = run_RPNI(data,
80+
automaton_type=automata_type[automata],
81+
algorithm=algorithm,
82+
print_info=False)
83+
84+
is_eq = self.prove_equivalence(learned_model)
85+
if not is_eq:
86+
print("Learned:")
87+
print(learned_model)
88+
print(algorithm, automata_type[automata])
89+
cex = compare_automata(learned_model, correct_automaton)
90+
print(cex)
91+
assert False
92+
93+
assert True

0 commit comments

Comments
 (0)