def sift(self, sequence): cur_dtree_node = self.DTree.root prev_dtree_node = None while not cur_dtree_node is None and not cur_dtree_node.isLeaf: # Figure out which branch we should follow seq = sequence + cur_dtree_node.suffix response = self.query(seq) prev_dtree_node = cur_dtree_node if response in cur_dtree_node.children: cur_dtree_node = cur_dtree_node.children[response] else: cur_dtree_node = None # If we end up on an empty node, we can add a new leaf pointing to the # state accessed by the given sequence if cur_dtree_node is None: new_acc_seq = sequence new_state = DFAState(f's{len(self.S)}') new_dtree_node = self.DTree.createLeaf(new_state) self.S[new_acc_seq] = new_state prev_dtree_node.add(response, new_dtree_node) cur_dtree_node = new_dtree_node assert cur_dtree_node is not None, "Oof this shouldn't happen" assert cur_dtree_node.isLeaf, "This should always be a leaf node" assert cur_dtree_node.state is not None, "Leaf nodes should always represent a state" return cur_dtree_node.state
def build_dfa(self): # Gather states from S S = self.S # The rows can function as index to the 'state' objects state_rows = set([tuple(self._get_row(s)) for s in S]) initial_state_row = tuple(self._get_row(tuple())) accepting_states_rows = set( [tuple(self._get_row(s)) for s in S if self.query(s)]) # Generate state names for convenience state_names = { state_row: f's{n + 1}' for (n, state_row) in enumerate(state_rows) } # Build the state objects and get the initial and accepting states states = { state_row: DFAState(state_names[state_row]) for state_row in state_rows } initial_state = states[initial_state_row] accepting_states = [states[a_s] for a_s in accepting_states_rows] # Add the connections between states A = [a for (a, ) in self.A] # Keep track of states already visited visited_rows = [] for s in S: s_row = tuple(self._get_row(s)) if s_row not in visited_rows: for a in A: sa_row = tuple(self._get_row(s + (a, ))) if sa_row in states.keys(): try: states[s_row].add_edge(a, states[sa_row]) except: # Can't add the same edge twice pass else: visited_rows.append(s_row) return DFA(initial_state, accepting_states)
def process_counterexample(self, counterexample): u, a, v = self.decompose(counterexample) # This state q_old needs to be split: q_old_state = self.get_state_from_sequence(u + a) q_old_acc_seq = self.get_access_sequence_from_state(q_old_state) # Store new state and access sequence q_new_acc_seq = self.get_access_sequence(u) + a q_new_state = DFAState(f's{len(self.S)}') assert q_new_acc_seq not in self.S self.S[q_new_acc_seq] = q_new_state ### update the DTree: # find the leaf corresponding to the state q_old q_old_leaf = self.DTree.getLeaf(q_old_state) # and create a new inner node, new_inner = self.DTree.createInner(v, temporary=True) # replace the old leaf node with the new inner node q_old_leaf.replace(new_inner) # check what branch the children should go response_q_old = self.query(q_old_acc_seq + v) response_q_new = self.query(q_new_acc_seq + v) # response_q_old = self.query(u + v) # response_q_new = self.query(q_new_acc_seq + v) assert response_q_new != response_q_old, "uh oh this should never happen" # prepare leaf node for the new state q_new_leaf = self.DTree.createLeaf(q_new_state) # Add the children to the corresponding branch of the new inner node new_inner.add(response_q_new, q_new_leaf) new_inner.add(response_q_old, q_old_leaf) print("splitty boi", q_old_state.name, q_new_state.name)
def setUp(self): s1 = DFAState('s1') s2 = DFAState('s2') s3 = DFAState('s3') s1.add_edge('a', s2) s1.add_edge('b', s1) s2.add_edge('a', s2) s2.add_edge('b', s3) s3.add_edge('a', s3) s3.add_edge('b', s3) self.dfa = DFA(s1, [s3])
def __init__(self, teacher: Teacher): # Access sequences S + state bookkeeping self.S = {tuple(): DFAState("s0")} super().__init__(teacher)
import tempfile from stmlearn.equivalencecheckers import BFEquivalenceChecker from stmlearn.learners import TTTDFALearner from stmlearn.suls import DFA, DFAState from stmlearn.teachers import Teacher # Set up a simple state machine (S1) =a> (S2) =b> ((S3)) s1 = DFAState('s1') s2 = DFAState('s2') s3 = DFAState('s3') s1.add_edge('a', s2) s2.add_edge('b', s3) sm = DFA(s1, [s3]) # Since we are learning a DFA, we need edges for the whole alphabet in every state s1.add_edge('b', s1) s2.add_edge('a', s2) s3.add_edge('a', s3) s3.add_edge('b', s3) # We are using the brute force equivalence checker eqc = BFEquivalenceChecker(sm) # Set up the teacher, with the system under learning and the equivalence checker teacher = Teacher(sm, eqc) # Set up the learner who only talks to the teacher
def setUp(self): # Set up an example mealy machine s1 = DFAState('1') s2 = DFAState('2') s3 = DFAState('3') s1.add_edge('a', s2) s1.add_edge('b', s1) s2.add_edge('a', s3) s2.add_edge('b', s1) s3.add_edge('a', s3) s3.add_edge('b', s1) self.dfa = DFA(s1, [s3])
def setUp(self): # Set up an example mealy machine s1 = DFAState('1') s1.add_edge('a', s1) s1.add_edge('b', s1) self.dfa = DFA(s1, [])