def __computesP(self, hypothesis): if hypothesis is None: raise Exception("Hypothesis cannot be None") self._logger.debug("Computing P") P = [] empty_word = Word([EmptyLetter()]) current_query = OutputQuery(empty_word) P.append(current_query) open_queries = deque([current_query]) close_queries = [] seen_states = set([hypothesis.initial_state]) while len(open_queries) > 0: query = open_queries.popleft() tmp_seen_states = set() for letter in self.input_letters: new_word = query.input_word + Word([letter]) query_z = OutputQuery(new_word) (output_word, visited_states) = hypothesis.play_query(query_z) close_queries.append(query_z) if visited_states[-1] not in seen_states: tmp_seen_states.add(visited_states[-1]) open_queries.append(query_z) seen_states.update(tmp_seen_states) P.extend(close_queries) return P
def _initialise_unrolling(self): self.cluster_information = {} initial_RState, pos = self.whiteboxrnn.get_first_RState() self.new_RStates = [ UnrollingInfo(self.proposed_dfa.initial_state, Word([EmptyLetter()]), initial_RState, pos) ]
def minimal_diverging_suffix( self, state1, state2 ): #gets series of letters showing the two states are different, # i.e., from which one state reaches accepting state and the other reaches rejecting state # assumes of course that the states are in the automaton and actually not equivalent res = None # just use BFS til you reach an accepting state # after experiments: attempting to use symmetric difference on copies with s1,s2 as the starting state, or even # just make and minimise copies of this automaton starting from s1 and s2 before starting the BFS, # is slower than this basic BFS, so don't seen_states = set() new_states = {(tuple(), (state1, state2))} while len(new_states) > 0: prefix, state_pair = new_states.pop() s1, s2 = state_pair if s1 != s2: # intersection of self.F and [s1,s2] is exactly one state, # meaning s1 and s2 are classified differently res = prefix break seen_states.add(state_pair) for a in self.input_letters: next_state_pair = (s1.visit(a)[1], s2.visit(a)[1]) #print("div_suf: next state pair: ", prefix, s1, s2, a, next_state_pair) next_tuple = (tuple(list(prefix) + [a]), next_state_pair) if not next_tuple in new_states and not next_state_pair in seen_states: new_states.add(next_tuple) return Word(res)
def find_counterexample(self, hypothesis): if hypothesis is None: raise Exception("Hypothesis cannot be None") self._logger.info( "Starting the RandomWalk Algorithm to search for a counter-example" ) i_step = 0 first_step_after_restart = True current_state = hypothesis.initial_state input_word = Word() hypothesis_output_word = Word() force_restart = False while i_step < self.max_steps: # should we restart if not first_step_after_restart: if force_restart or random.random() < self.restart_probability: current_state = hypothesis.initial_state first_step_after_restart = True counterexample_query = self.__check_equivalence( input_word, hypothesis_output_word) if counterexample_query is not None: return counterexample_query input_word = Word() hypothesis_output_word = Word() force_restart = False else: first_step_after_restart = False try: (new_state, input_letter, output_letter) = self.__walk(current_state) current_state = new_state input_word.letters.append(input_letter) hypothesis_output_word.letters.append(output_letter) except Exception as e: self._logger.warn(e) force_restart = True i_step += 1
def _counterexample_from_cluster_conflict(self, old_info, new_info): q1 = old_info.dfa_state q2 = new_info.dfa_state prefixes = old_info.paths + new_info.paths suffix = self.parent.minimal_diverging_suffix(q1, q2) #print(" --- ", prefixes, suffix, prefixes[0] + suffix, type(prefixes[0]), type(suffix)) return self._get_counterexample_from( [Word(p.letters + suffix.letters) for p in prefixes])
def __compute_distinguishable_string(self, hypothesis, couple): self._logger.debug( "Computes the distinguishable string for state couple '{}'".format( couple)) if hypothesis is None: raise Exception("Hypothesis cannot be None") if couple is None: raise Exception("couple cannot be None") self._logger.debug( "Computing distinguishing strings for states {}".format(couple)) queries_to_test = deque([]) empty_word = Word([EmptyLetter()]) z_query = OutputQuery(empty_word) for letter in self.input_letters: new_word = z_query.input_word + Word([letter]) queries_to_test.append(OutputQuery(new_word)) distinguishable_query = z_query done = False i = 0 while not done: query = queries_to_test.popleft() if i > self.max_states * self.max_states: break if not self.__is_distinguishable_states(hypothesis, query, couple): done = False for letter in self.input_letters: new_query = OutputQuery(query.input_word + Word([letter])) queries_to_test.append(new_query) else: done = True distinguishable_query = query i = i + 1 return distinguishable_query
def get_output_word(self, input_word): if input_word is None: raise Exception("Input word cannot be None") for root in self.roots: try: w = Word(root.traverse(input_word.letters)) self._logger.info("I = {} > O = {}".format(input_word, w)) return w except Exception: pass raise Exception("No path found")
def submit_word(self, word): self._logger.debug( "Submiting word '{}' to the fake target".format(word)) if self.automata is None: raise Exception("Automata cannot be None") current_state = self.automata.initial_state output_letters = [] for letter in word.letters: try: (current_state, output_letter) = self._next_state(current_state, letter) except Exception: output_letter = EmptyLetter() output_letters.append(output_letter) output_word = Word(output_letters) return output_word
def _add_children_states(self, cluster): state_info = self.cluster_information[cluster] if not state_info.explored: # we explore a state only the first time we successfully visit and process it, and we store a state's # information in self.cluster_information only if we have successfully processed it. RState = state_info.RStates[0] state_info.explored = True for char in self.parent.input_letters: next_RState, pos = self.whiteboxrnn.get_next_RState( RState, char) path = state_info.paths[0] + Word([ char, ]) #XXXjc test pos = self.whiteboxrnn.submit_word(path) # we only ever explore a state the first # time we find it, so, with the first path in its list of reaching paths next_dfa_state = state_info.dfa_state.visit(char)[ 1] #self.proposed_dfa.delta[state_info.dfa_state][char] self.new_RStates.append( UnrollingInfo(next_dfa_state, path, next_RState, pos))
def __computesZ(self, hypothesis, W): """it follows the formula Z= W U (X^1.W) U .... U (X^(m-1-n).W) U (W^(m-n).W) """ if hypothesis is None: raise Exception("Hypothesis cannot be None") if W is None: raise Exception("W cannot be None") self._logger.debug("Computing Z") Z = [] Z.extend(W) states = hypothesis.get_states() v = self.max_states - len(states) if v < 0: v = 0 self._logger.debug("V= {}".format(v)) output_queries = [] for input_letter in self.input_letters: output_query = OutputQuery(word=Word([input_letter])) output_queries.append(output_query) X = dict() X[0] = W for i in range(1, v + 1): self._logger.debug("Computing X^{}".format(i)) X[i] = [] previous_X = X[i - 1] for x in previous_X: X[i].extend(x.multiply(output_queries)) for w in W: for xi in X[i]: if not xi in Z: Z.append(xi) return Z
def play_word(self, input_word, starting_state=None): """This method can be used to play the specified word accross the current automata. It returns a tupple made of the output_word and the visited state captured while visiting the automata >>> from mylstar.Letter import Letter, EmptyLetter >>> from mylstar.Word import Word >>> from mylstar.automata.State import State >>> from mylstar.automata.Transition import Transition >>> from mylstar.automata.Automata import Automata >>> l_lambda = EmptyLetter() >>> l_a = Letter('a') >>> l_b = Letter('b') >>> l_0 = Letter(0) >>> l_1 = Letter(1) >>> s0 = State("S0") >>> s1 = State("S1") >>> s2 = State("S2") >>> s3 = State("S3") >>> t1 = Transition("T1", s3, l_a, l_0) >>> t2 = Transition("T2", s1, l_b, l_0) >>> s0.transitions = [t1, t2] >>> t3 = Transition("T3", s0, l_a, l_1) >>> t4 = Transition("T4", s2, l_b, l_1) >>> s1.transitions = [t3, t4] >>> t5 = Transition("T5", s3, l_a, l_0) >>> t6 = Transition("T6", s0, l_b, l_0) >>> s2.transitions = [t5, t6] >>> t7 = Transition("T7", s3, l_a, l_1) >>> t8 = Transition("T8", s3, l_b, l_1) >>> s3.transitions = [t7, t8] >>> automata = Automata(s0) >>> print(automata.play_word(Word([l_a, l_a, l_a]))[0]) [Letter(0), Letter(1), Letter(1)] >>> print(automata.play_word(Word([l_b, l_b, l_b]))[0]) [Letter(0), Letter(1), Letter(0)] >>> print(automata.play_word(Word([l_b, l_a, l_b, l_a, l_b]))[0]) [Letter(0), Letter(1), Letter(0), Letter(1), Letter(0)] """ if input_word is None or len(input_word) == 0: raise Exception("Input word cannot be None or empty") #if input_word == Word([EmptyLetter()]) and starting_state == None: # self._logger.debug("Received Word([EmptyLetter()]), returning initial state") # return (Word([EmptyLetter()]), self.initial_state) if starting_state is None: current_state = self.initial_state else: current_state = starting_state self._logger.debug("Playing word '{}'".format(input_word)) output_letters = [] visited_states = [] for letter in input_word.letters: if letter == EmptyLetter(): output_letters.append(EmptyLetter()) visited_states.append(current_state) output_state = current_state else: (output_letter, output_state) = current_state.visit(letter) output_letters.append(output_letter) visited_states.append(output_state) current_state = output_state output_word = Word(letters=output_letters) return (output_word, visited_states)