Skip to content

Commit

Permalink
fix product with an empty list in W and WP oracles
Browse files Browse the repository at this point in the history
  • Loading branch information
emuskardin committed Jan 16, 2025
1 parent b592757 commit 9da4c3f
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 16 deletions.
9 changes: 5 additions & 4 deletions aalpy/oracles/WMethodEqOracle.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
from itertools import product
from random import shuffle, choice, randint

from aalpy.base.Oracle import Oracle
from aalpy.base.SUL import SUL
from aalpy.utils.HelperFunctions import product_with_possible_empty_iterable


class WMethodEqOracle(Oracle):
"""
Equivalence oracle based on characterization set/ W-set. From 'Tsun S. Chow. Testing software design modeled by
finite-state machines'.
"""

def __init__(self, alphabet: list, sul: SUL, max_number_of_states, shuffle_test_set=True):
"""
Args:
Expand All @@ -35,9 +36,9 @@ def find_cex(self, hypothesis):

middle = []
for i in range(self.m + 1 - len(hypothesis.states)):
middle.extend(list(product(self.alphabet, repeat=i)))
middle.extend(list(product_with_possible_empty_iterable(self.alphabet, repeat=i)))

for seq in product(transition_cover, middle, hypothesis.characterization_set):
for seq in product_with_possible_empty_iterable(transition_cover, middle, hypothesis.characterization_set):
inp_seq = tuple([i for sub in seq for i in sub])
if inp_seq not in self.cache:
self.reset_hyp_and_sul(hypothesis)
Expand All @@ -53,7 +54,6 @@ def find_cex(self, hypothesis):
self.sul.post()
return inp_seq[:ind + 1]
self.cache.add(inp_seq)


return None

Expand All @@ -64,6 +64,7 @@ class RandomWMethodEqOracle(Oracle):
Random walks stem from fixed prefix (path to the state). At the end of the random
walk an element from the characterization set is added to the test case.
"""

def __init__(self, alphabet: list, sul: SUL, walks_per_state=12, walk_len=12):
"""
Args:
Expand Down
39 changes: 27 additions & 12 deletions aalpy/oracles/WpMethodEqOracle.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from aalpy.base.Oracle import Oracle
from aalpy.base.SUL import SUL
from itertools import product, chain, tee
from itertools import chain, tee

from aalpy.utils.HelperFunctions import product_with_possible_empty_iterable


def state_characterization_set(hypothesis, alphabet, state):
Expand All @@ -21,30 +23,35 @@ def state_characterization_set(hypothesis, alphabet, state):
return result


def i_star(alph, upto):
def i_star(alphabet, max_seq_len):
"""
Return an iterator that generates all possible sequences of length upto from the given alphabet.
Args:
alph: input alphabet
upto: maximum length of the sequences
alphabet: input alphabet
max_seq_len: maximum length of the sequences
"""
return chain(*(product(alph, repeat=i) for i in range(upto)))
return chain(*(product_with_possible_empty_iterable(alphabet, repeat=i) for i in range(max_seq_len)))


def second_phase_it(hyp, alph, difference, middle):
def second_phase_it(hyp, alphabet, difference, middle):
"""
Return an iterator that generates all possible sequences for the second phase of the Wp-method.
Args:
hyp: hypothesis automaton
alph: input alphabet
alphabet: input alphabet
difference: set of sequences that are in the transition cover but not in the state cover
middle: iterator that generates all possible sequences of length upto from the given alphabet
"""
for t, mid in product(difference, middle):
state_mapping = {}
for t, mid in product_with_possible_empty_iterable(difference, middle):
_ = hyp.execute_sequence(hyp.initial_state, t + mid)
state = hyp.current_state
char_set = state_characterization_set(hyp, alph, state)
concatenated = product([t], [mid], char_set)
if state not in state_mapping:
char_set = state_characterization_set(hyp, alphabet, state)
state_mapping[state] = char_set
else:
char_set = state_mapping[state]
concatenated = product_with_possible_empty_iterable([t], [mid], char_set)
for el in concatenated:
yield el

Expand All @@ -68,18 +75,26 @@ def find_cex(self, hypothesis):
for state in hypothesis.states
for letter in self.alphabet
)

state_cover = set(state.prefix for state in hypothesis.states)
difference = transition_cover.difference(state_cover)

# two views of the same iterator
middle_1, middle_2 = tee(i_star(self.alphabet, self.m), 2)
middle_1, middle_2 = tee(i_star(self.alphabet, self.m - hypothesis.size + 1), 2)

# first phase State Cover * Middle * Characterization Set
first_phase = product(state_cover, middle_1, hypothesis.characterization_set)
first_phase = product_with_possible_empty_iterable(state_cover, middle_1, hypothesis.characterization_set)

# second phase (Transition Cover - State Cover) * Middle * Characterization Set
# of the state that the prefix leads to
second_phase = second_phase_it(hypothesis, self.alphabet, difference, middle_2)

test_suite = chain(first_phase, second_phase)

l = 0
for seq in test_suite:
print(l)
l += 1
inp_seq = tuple([i for sub in seq for i in sub])
if inp_seq not in self.cache:
self.reset_hyp_and_sul(hypothesis)
Expand Down
9 changes: 9 additions & 0 deletions aalpy/utils/HelperFunctions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import random
import string
from itertools import product
from collections import defaultdict


Expand Down Expand Up @@ -376,3 +377,11 @@ def generate_input_output_data_from_vpa(vpa, num_sequances=4000, min_seq_len=1,
input_output_sequances.append(list(zip(sequance, outputs)))

return input_output_sequances


def product_with_possible_empty_iterable(*iterables, repeat=1):
"""
Words like regular product, but if one of the iterables is empty it will just ignore it, instead of returning [].
"""
non_empty_iterables = [it for it in iterables if it]
return product(*non_empty_iterables, repeat=repeat)

0 comments on commit 9da4c3f

Please sign in to comment.