def __sup_to_minimize_with_distance_function(self, artefact, variablesFunction): list_of_formula = [] if artefact == ANTI_ALIGNMENT: for d in range(0, self.__max_d + 1): list_of_d = [] for j in range(0, len(self.__traces)): # delta_j1nnd and delta_j2nnd and ... delta_jlnnd list_of_d.append(variablesFunction(j, d)) # not di or ( dji and dji ... dji) not_di_or_list_of_and = Or( [], [self.__vars.get(BOOLEAN_VAR_SUP, [d])], [And(list_of_d, [], [])]) list_of_formula.append(not_di_or_list_of_and) if artefact == MULTI_ALIGNMENT: for d in range(0, self.__max_d + 1): list_of_d = [] for j in range(0, len(self.__traces)): # not delta_j1nnd or not delta_j2nnd or ... not delta_jlnnd list_of_d.append(variablesFunction(j, d)) # di or ( dji or dji ... dji) not_di_or_list_of_and = Or( [self.__vars.get(BOOLEAN_VAR_SUP, [d])], [], [And([], list_of_d, [])]) list_of_formula.append(not_di_or_list_of_and) return list_of_formula
def is_action(places, transitions, m0, i, m_ip, tau_it, space_between_fired): ''' The is_action method used by is_run creates the formula of each instant. Which transition fired ? :param places (list) : :param transitions (list) : :param m0 (marking) : initial marking :param i (int) : instant in the run :param m_ip (function) : function to get the number of the boolean variables of the markings, see variablesGenerator. :param tau_it (function) : function to get the number of the boolean variables of the transitions, see variablesGenerator. :return: ''' # only one transition is true at instant i or_formulas = [] for t in transitions: or_formulas.append( And([tau_it([i, transitions.index(t)])], [ tau_it([i, transitions.index(t2)]) for t2 in transitions if t != t2 ], [])) formulas = [Or([], [], or_formulas)] # if transition t fires at instant i, then we have the good marking for t in transitions: formulas.append( Or([], [tau_it([i, transitions.index(t)])], [is_transition(places, t, i, m_ip, space_between_fired)])) return And([], [], formulas)
def maxDistance2(vars, diff1, diff2, max_d, size_of_run): list_to_size_of_run = list(range(1, size_of_run * 2 + 1)) max_distance = max_d # IDEA : there are at least max_distance number of false variables combinaisons_of_instants = list( itertools.combinations(list_to_size_of_run, max_distance)) distFalseVariables = [] for instants in combinaisons_of_instants: list_distances = [] for i in instants: if i <= size_of_run: list_distances.append(diff1([i])) else: list_distances.append(diff2([i - size_of_run])) list_distances2 = [] for i in range(1, size_of_run * 2 + 1): if i not in instants: if i <= size_of_run: list_distances2.append(diff1([i])) else: list_distances2.append(diff2([i - size_of_run])) distFalseVariables.append( Or([], list_distances, [And([], list_distances2, [])])) return Or([], [], distFalseVariables)
def initialisation_ReducedForAntiAlignment(transitions, silent_transitions, tau_it, lambda_jia, djiid, j, size_of_run, wait_transition, max_d): ''' Initialisation of the edit distance reduced for anti-alignmet. This is the 3 first axioms of the _Encoding Conformance Checking Artefacts in SAT_ paper :param transitions (list) : list of transitions :param silent_transitions (list) : list of the silent transitions :param tau_it (fun) : function that returns the boolean variable number of the firing transition t at an instant i :param lambda_jia (fun) : function that returns the boolean variable number of the letter of trace j at instant i :param djiid (fun) : function that returns the boolean variable that says at instant i and i of we have d distance :param j (int) : jth trace :param size_of_run (int) : maximal size of a run :param wait_transition (transition) : to finish words :param max_d (int): heuristic :return: formula ''' def t(transition): return transitions.index(transition) positives = [] # diid is true for d = 0 for i_m in range(0, size_of_run + 1): for i_t in range(0, size_of_run + 1): positives.append(djiid([j, i_m, i_t, 0])) # diid is false for 1 1 d and d >0 negatives = [djiid([j, 0, 0, d]) for d in range(1, max_d + 1)] formulas = [] for d in range(0, max_d): for i_m in range(0, size_of_run): # (i_m <> w and i_m <> tau ) <=> (d im+1 0 d+1 => d im 0 d ) condition = [tau_it([i_m + 1, t(wait_transition)])] for st in silent_transitions: condition.append(tau_it([i_m + 1, t(st)])) this_condition = condition this_condition.append(djiid([j, i_m, 0, d])) i_t_null_and_i_m_cost = Or(this_condition, [djiid([j, i_m + 1, 0, d + 1])], []) formulas.append(i_t_null_and_i_m_cost) # (i_m == w or i_m == tau ) <=> (d im+1 0 d => d im 0 d ) i_t_null_and_i_m_dont_cost = Or([djiid([j, i_m, 0, d])], [djiid([j, i_m + 1, 0, d])], [ And([], condition, []) ]) formulas.append(i_t_null_and_i_m_dont_cost) for i_t in range(0, size_of_run): # i_t <> w <=> (d 0 it+1 d+1 => d 0 it d ) i_m_null_and_i_t_cost = Or( [lambda_jia([j, i_t + 1, t(wait_transition)]), djiid([j, 0, i_t, d])], [djiid([j, 0, i_t + 1, d + 1])], []) formulas.append(i_m_null_and_i_t_cost) # i_t == w <=> (d 0 it+1 d => d 0 it d ) i_m_null_and_i_t_dont_cost = Or([djiid([j, 0, i_t, d])], [lambda_jia([j, i_t + 1, t(wait_transition)]), djiid([j, 0, i_t + 1, d])], []) formulas.append(i_m_null_and_i_t_dont_cost) return And(positives, negatives, formulas)
def bodyHammingDistance_reducedForAntiAlignment(transitions, silent_transitions, vars, nbTraces, size_of_run): ''' Reduced formula of hamming distance for anti-alignment :param transitions (list) : the transitions :param silent_transitions (list) : the silent transitions :param variables (variablesGenerator) : to creates the variables numbers :param nbTraces (int) : number of traces :param size_of_run (int) : maximal size of run :return: formula ''' formulas = [] for j in range(0, nbTraces): for i in range(1, size_of_run + 1): for t in range(0, len(transitions)): if transitions[t] not in silent_transitions: # (tau_i,t and lambda_i,t ) => not diff_i create_diff = Or([], [vars.get(BOOLEAN_VAR_HAMMING_DISTANCE, [j, i])], [ Or([], [vars.get(BOOLEAN_VAR_FIRING_TRANSITION_PN, [i, t]), vars.get(BOOLEAN_VAR_TRACES_ACTIONS, [j, i, t]), ], []) ]) else: # tau_ti => not diff_i create_diff = Or([], [vars.get(BOOLEAN_VAR_FIRING_TRANSITION_PN, [i, t]), vars.get(BOOLEAN_VAR_HAMMING_DISTANCE, [j, i])], []) formulas.append(create_diff) return formulas
def bodyHammingDistance(transitions, silent_transitions, vars, nbTraces, size_of_run): ''' Exact formula of hamming distance. :param transitions (list) : the transitions :param silent_transitions (list) : the silent transitions :param variables (variablesGenerator) : to creates the variables numbers :param nbTraces (int) : number of traces :param size_of_run (int) : maximal size of run :return: formula ''' formulas = [] for j in range(0, nbTraces): for i in range(1, size_of_run + 1): for t in range(0, len(transitions)): if transitions[t] not in silent_transitions: create_diff = Or([], [], [ And([vars.get(BOOLEAN_VAR_FIRING_TRANSITION_PN, [i, t])], [], [ Or([vars.get(BOOLEAN_VAR_TRACES_ACTIONS, [j, i, t]), vars.get(BOOLEAN_VAR_HAMMING_DISTANCE, [j, i])], [], []) ]), And([], [vars.get(BOOLEAN_VAR_FIRING_TRANSITION_PN, [i, t]), vars.get(BOOLEAN_VAR_TRACES_ACTIONS, [j, i, t]), vars.get(BOOLEAN_VAR_HAMMING_DISTANCE, [j, i])], []) ]) else: create_diff = Or([], [vars.get(BOOLEAN_VAR_FIRING_TRANSITION_PN, [i, t]), vars.get(BOOLEAN_VAR_HAMMING_DISTANCE, [j, i])], []) formulas.append(create_diff) return formulas
def __getDiffTracesCentroids(self, chi_jia, diffl_ji, diffm_ji, lambda_jia): ''' This function that defines the difference between the traces and its centroid and calls __maxDiffTracesCentroids(). :param chi_jia (function) :param diff_ji (function) :param lambda_jia (function) :return: list of formula ''' formulas = [] for j in range(0, len(self.__traces)): aDiffPerInstant = [] for i in range(1, self.__size_of_run + 1): # for each instant, a transition is true and there is or not a diff for t in range(0, len(self.__transitions)): # if silent transition : diffjit is false if self.__transitions[t] in self.__silent_transititons: indexOfWaitModel = self.__transitions.index( self.__wait_transition_model) indexOfWaitTrace = self.__transitions.index( self.__wait_transition_trace) diffjit = Or([ diffl_ji([j, i]), lambda_jia([j, i, indexOfWaitModel]), lambda_jia([j, i, indexOfWaitTrace]) ], [chi_jia([j, i, t])], []) # chi_jia => lambda_jia or diff_ji elif self.__transitions[t] == self.__wait_transition_model: diffjit = Or([diffl_ji([j, i]), lambda_jia([j, i, t])], [chi_jia([j, i, t])], []) elif self.__transitions[t] == self.__wait_transition_trace: diffjit = Or([diffm_ji([j, i])], [chi_jia([j, i, t])], []) else: indexOfWaitTrace = self.__transitions.index( self.__wait_transition_trace) diffjit = Or([], [], [ Or([lambda_jia([j, i, t])], [chi_jia([j, i, t])], [ And([diffl_ji([j, i]), diffm_ji([j, i])], [], []) ]), And([ diffm_ji([j, i]), lambda_jia([j, i, indexOfWaitTrace]), chi_jia([j, i, t]) ], [], []) ]) aDiffPerInstant.append(diffjit) diffPerJ = And([], [], aDiffPerInstant) formulas.append(diffPerJ) # then there is maximal number of diff : self.__maxDiffTracesCentroids(formulas) return formulas
def for_hamming_distance_aux_supd_multi(vars, lenTraces, max_d, size_of_run): list_of_formula=[] list_to_size_of_run= list(range(1,size_of_run+1)) not_d_or_and_diff=[] for j in range (0, lenTraces): for d in range(1,min(max_d + 1,size_of_run+1)): combinaisons_of_instants=list(itertools.combinations(list_to_size_of_run,d)) and_sub_instants=[] for sub_list_of_instants in combinaisons_of_instants: instants_to_combine=[] for instant in list(sub_list_of_instants): instants_to_combine.append(vars.get(BOOLEAN_VAR_HAMMING_DISTANCE, [j, instant])) and_sub_instants.append(Or([],instants_to_combine,[])) not_d_or_and_diff.append(Or([vars.get(BOOLEAN_VAR_HAMMING_SUP_AUX, [j, d])], [], [And([], [], and_sub_instants)])) list_of_formula.append(And([],[],not_d_or_and_diff)) return list_of_formula
def __maxDiffTracesCentroids(self, formulas): ''' This function uses self.__max_d that determines the maximal distance of the trace to its centroid. Idea of the threshold : there are at least N diff variables false per trace. :param formulas (list of formula to fill) :return: void ''' # this function uses combinations of itertools to get all the combinations : this is better than parameter # at_most of pysat library list_to_size_of_run = list(range(1, (self.__size_of_run * 2) + 1)) max_distance = (self.__size_of_run * 2) - self.__max_d # IDEA : there are at least max_distance number of false variables combinaisons_of_instants = list( itertools.combinations(list_to_size_of_run, max_distance)) for j in range(0, len(self.__traces)): distFalseVariables = [] for instants in combinaisons_of_instants: list_distances = [] for i in instants: if i <= self.__size_of_run: list_distances.append( self.__vars.get(BOOLEAN_VAR_DIFF_l, [j, i])) else: list_distances.append( self.__vars.get(BOOLEAN_VAR_DIFF_m, [j, (i - self.__size_of_run)])) distFalseVariables.append(And([], list_distances, [])) formulas.append(Or([], [], distFalseVariables))
def is_action_for_j(j, trace_places, pn_transitions, trace_transitions, i, m_ip, tau_it): ''' The is_action method used by is_run creates the formula of each instant. Which transition fired ? :param places (list) : :param pn_transitions (list) : we need all the alphabet to refuse transitions that aren't in trace :param trace_transitions (list) : transitions of the trace :param i (int) : instant in the run :param m_ip (function) : function to get the number of the boolean variables of the markings, see variablesGenerator. :param tau_it (function) : function to get the number of the boolean variables of the transitions, see variablesGenerator. :return: ''' # only one transition is true at instant i # lazy comment : this verifies that alphabet is complet : all action not executed has to be taken into account or_formulas = [] formulas = [] label_transitions = [t.label for t in pn_transitions] copie_transitions = [t for t in pn_transitions] transitions_already_done = [] for t in trace_transitions: if t not in transitions_already_done: other_transitions_with_same_label = [] for t2 in trace_transitions: if t2.label == t.label: other_transitions_with_same_label.append(t2) transitions_already_done.append(t2) if t.label in label_transitions: index_of_t = label_transitions.index(t.label) else: index_of_t = None # should never happen copie_transitions.remove( pn_transitions[(label_transitions.index(t.label))]) or_formulas.append(And([tau_it([j, i, index_of_t])], [], [])) implication = [] for t2 in other_transitions_with_same_label: implication.append( is_transition_for_j(j, trace_places, t2, i, m_ip)) formulas.append( Or([], [tau_it([j, i, index_of_t])], implication)) formulas.append(Or([], [], or_formulas)) formulas.append( And([], [ tau_it([j, i, pn_transitions.index(r)]) for r in copie_transitions ], [])) return And([], [], formulas)
def is_action_centroid(j, places, transitions, i, m_jip, tau_jip, c_kt, chi_jk, nb_clusters): ''' @see pnToFormula.py, is_action_centroid says if transition is fired. :param j (int) : index of trace :param places (list) : places of the petri net, indexes are important :param transitions (list) : transitions of the petri net, indexes are important :param i (int) : instant in the run of the centroid :param m_jip (function) : @see variablesGenerator.py, function chi_jk gets BOOLEAN_VAR_CHI_MARKINGS variables :param tau_jip (function) : @see variablesGenerator.py, function chi_jk gets BOOLEAN_VAR_CHI_TRANSITIONS variables :param c_kt (function) : @see variablesGenerator.py, function chi_jk gets BOOLEAN_VAR_K_CONTAINS_T variables :param chi_jk (function) : @see variablesGenerator.py, function chi_jk gets BOOLEAN_VAR_J_IN_K variables :param nb_clusters (int) : number of cluster :return: ''' # a transition fires and only one aTransitionPerInstant = [ And([tau_jip([j, i, t])], [ tau_jip([j, i, t2]) for t2 in range(len(transitions)) if t != t2 ], []) for t in range(len(transitions)) ] formulas = [Or([], [], aTransitionPerInstant)] # runs is_transition for the fired transition indexOfTraceWait = transitions.index(self.__wait_transition_trace) for t in range(len(transitions)): # WAIT_TRANSITION_TRACE is forbidden for centroid if t == indexOfTraceWait: formulas.append(And([], [tau_jip([j, i, t])], [])) else: formulas.append( Or([], [tau_jip([j, i, t])], [ And([], [], [ is_transition_centroid( j, places, transitions[t], i, m_jip), in_cluster_of_j(t, c_kt, j, chi_jk, nb_clusters) ]) ])) return And([], [], formulas)
def distanceNets(vars, size_of_run, tau1, tau2, pn1_transitions, pn2_transitions, w1, w2): formula = [] vars.add(BOOLEAN_VAR_DIFF1, [(1, size_of_run + 1)]) vars.add(BOOLEAN_VAR_DIFF2, [(1, size_of_run + 1)]) for i in range(1, size_of_run + 1): for t1 in pn1_transitions: ''' listOfSameLabels=[tau2([i,pn2_transitions.index(t2)]) for t2 in pn2_transitions if t2.label==t1.label] listOfSameLabels.append(vars.getFunction(BOOLEAN_VAR_DIFF)([i])) formula.append(Or(listOfSameLabels,[tau1([i,pn1_transitions.index(t1)]) ],[])) ''' if t1 != w1: listOfSameLabels = [ tau2([i, pn2_transitions.index(t2)]) for t2 in pn2_transitions if t2.label == t1.label ] listOfSameLabels.append(tau2([i, pn2_transitions.index(w2)])) formula.append( Or(listOfSameLabels, [tau1([i, pn1_transitions.index(t1)])], [ And([ vars.getFunction(BOOLEAN_VAR_DIFF1)([i]), vars.getFunction(BOOLEAN_VAR_DIFF2)([i]) ], [], []) ])) formula.append( Or([vars.getFunction(BOOLEAN_VAR_DIFF1)([i])], [ tau2([i, pn2_transitions.index(w2)]), tau1([i, pn1_transitions.index(t1)]) ], [])) else: formula.append( Or([ vars.getFunction(BOOLEAN_VAR_DIFF2)([i]), tau2([i, pn2_transitions.index(w2)]) ], [tau1([i, pn1_transitions.index(t1)])], [])) return And([], [], formula)
def in_cluster_of_j(tr, c_kt, j, chi_jk, nb_clusters): ''' Subfunction of __createCentroids. in_cluster_of_j function affects a transition to the cluster of the trace. :param tr (Transition) :param c_kt (function) : @see variablesGenerator.py, function c_kt(k,t) gets BOOLEAN_VAR_K_CONTAINS_T variables :param j (int) : index of the current trace :param chi_jk (function) : @see variablesGenerator.py, function chi_jk gets BOOLEAN_VAR_J_IN_K variables :param nb_clusters (int) : number of cluster :return: ''' # BOOLEAN_VAR_J_IN_K => BOOLEAN_VAR_K_CONTAINS_T return And([], [], [ Or([c_kt([k, tr])], [chi_jk([j, k])], []) for k in range(0, nb_clusters) ])
def is_transition(places, transition, i, m_ip, space_between_fired): ''' The is_transition method used by is_action creates the formula of a firing transition. Which marking is needed ? :param places (list) : :param transitions (list) : :param i (int) : instant in the run :param m_ip (function) : function to get the number of the boolean variables of the markings, see variablesGenerator. :return: ''' formulas = [] prePlaces = [a.source for a in transition.in_arcs] postPlaces = [a.target for a in transition.out_arcs] for p in places: if p in prePlaces and p in postPlaces: formulas.append( And([ m_ip([i, places.index(p)]), m_ip([i - space_between_fired, places.index(p)]) ], [], [])) elif p in prePlaces and p not in postPlaces: formulas.append( And([m_ip([i - space_between_fired, places.index(p)])], [m_ip([i, places.index(p)])], [])) elif p not in prePlaces and p in postPlaces: formulas.append( And([m_ip([i, places.index(p)])], [m_ip([i - space_between_fired, places.index(p)])], [])) elif p not in prePlaces and p not in postPlaces: formulas.append( Or([], [], [ And([ m_ip([i, places.index(p)]), m_ip([i - space_between_fired, places.index(p)]) ], [], []), And([], [ m_ip([i, places.index(p)]), m_ip([i - space_between_fired, places.index(p)]) ], []) ])) return And([], [], formulas)
def __commonTransitions(self, common_kkt, ckt): ''' When two clusters share a transition, BOOLEAN_VAR_COMMON_T are True. :paramc common_kkt (function) :param ckt (function) :return list of formula ''' listOfCommunTransitionsFormulas = [] for k1 in range(0, self.__nb_clusters): for k2 in range(k1 + 1, self.__nb_clusters): for t in range(len(self.__transitions)): # (c_k1t and c_k2t) => common_k1k2t haveATransitionInCommon = Or( [common_kkt([k1, k2, t])], [ckt([k1, t]), ckt([k2, t])], []) listOfCommunTransitionsFormulas.append( haveATransitionInCommon) return listOfCommunTransitionsFormulas
def numberOfWaitInRun(vars, size_of_run, tau1, pn1_transitions, w1, we): list_to_size_of_run = list(range(1, size_of_run * 2 + 1)) minw1 = int((size_of_run) / 2) # IDEA : there are at least max_distance number of w1 variables to false combinaisons_of_instants = list( itertools.combinations(list_to_size_of_run, minw1)) w1ToTrue = [] for instants in combinaisons_of_instants: listOfW1ToTrue = [] for i in instants: if i <= int(size_of_run): listOfW1ToTrue.append(tau1([i, pn1_transitions.index(w1)])) else: listOfW1ToTrue.append( tau1([i - int(size_of_run), pn1_transitions.index(we)])) w1ToTrue.append(And(listOfW1ToTrue, [], [])) return Or([], [], w1ToTrue)
def is_transition_centroid(j, places, tr, i, m_ip): ''' This function verifies marking to fire transition of a centroid. :param j (int) : index of centroid :param places (list) : list of places :param tr (Transition) : transition that wants to fire :param i (int) : instant of firing :param m_ip (function) : @see @variablesGenerator.py function to get BOOLEAN_VAR_CHI_MARKINGS variables ''' formulas = [] prePlaces = [a.source for a in tr.in_arcs] postPlaces = [a.target for a in tr.out_arcs] # token game for p in places: if p in prePlaces and p in postPlaces: formulas.append( And([ m_ip([j, i, places.index(p)]), m_ip([j, i - 1, places.index(p)]) ], [], [])) elif p in prePlaces and p not in postPlaces: formulas.append( And([m_ip([j, i - 1, places.index(p)])], [m_ip([j, i, places.index(p)])], [])) elif p not in prePlaces and p in postPlaces: formulas.append( And([m_ip([j, i, places.index(p)])], [m_ip([j, i - 1, places.index(p)])], [])) elif p not in prePlaces and p not in postPlaces: formulas.append( Or([], [], [ And([ m_ip([j, i, places.index(p)]), m_ip([j, i - 1, places.index(p)]) ], [], []), And([], [ m_ip([j, i, places.index(p)]), m_ip([j, i - 1, places.index(p)]) ], []) ])) return And([], [], formulas)
def __tracesInAClusterOnly(self, inC_j, chi_jk): ''' Verifies that j is in a unique cluster or any :param inC_j (function) :param chi_jk (function) ''' formulas = [] for j in range(0, len(self.__traces)): inCorNot = [] for k1 in range(0, self.__nb_clusters): # if in k1 then not in other k inCorNot.append( And([inC_j([j]), chi_jk([j, k1])], [ chi_jk([j, k]) for k in range(0, self.__nb_clusters) if k != k1 ], [])) # if in any k, then not inC allKNot = [chi_jk([j, k]) for k in range(0, self.__nb_clusters)] allKNot.append(inC_j([j])) inCorNot.append(And([], allKNot, [])) formulas.append(Or([], [], inCorNot)) return formulas
def recursionEditDistance_reducedForMultiAlignment(transitions, silent_transitions, tau_it, lambda_jia, djiid, j, size_of_run, wait_transition, max_d): ''' Initialisation of the edit distance reduced for multi-alignment. This is the 2 lasts axioms of the _Encoding Conformance Checking Artefacts in SAT_ paper :param transitions (list) : list of transitions :param silent_transitions (list) : list of the silent transitions :param tau_it (fun) : function that returns the boolean variable number of the firing transition t at an instant i :param lambda_jia (fun) : function that returns the boolean variable number of the letter of trace j at instant i :param djiid (fun) : function that returns the boolean variable that says at instant i and i of we have d distance :param j (int) : jth trace :param size_of_run (int) : maximal size of a run :param wait_transition (transition) : to finish words :param max_d (int): heuristic :return: formula ''' def t(transition): return transitions.index(transition) formulas = [] for i_m in range(0, size_of_run): for i_t in range(0, size_of_run): for d in range(0, max_d): # letters are equals i_t+1 == i_m+1 => (d i_t i_m d => d i_t+1 i_m+1 d) letters_are_equals = Or([djiid([j, i_m + 1, i_t + 1, d+1])], [djiid([j, i_m, i_t, d+1])], [And([], [], [Or([], [tau_it([i_m + 1, t]), lambda_jia([j, i_t + 1, t])], []) for t in range(0, len(transitions))] )]) formulas.append(letters_are_equals) # letters are diff : i_t+1 == i_m+1 => (d i_t i_m d => d i_t+1 i_m+1 d) condition = [tau_it([i_m + 1, t(wait_transition)]), lambda_jia([j, i_t + 1, t(wait_transition)])] for st in silent_transitions: condition.append(tau_it([i_m + 1, t(st)])) condition.append(djiid([j, i_m + 1, i_t + 1, d + 1])) letters_are_diff = Or(condition, [djiid([j, i_m + 1, i_t, d]), djiid([j, i_m, i_t + 1, d])], [And([tau_it([i_m + 1, t]), lambda_jia([j, i_t + 1, t])], [], []) for t in range(0, len(transitions))]) formulas.append(letters_are_diff) # ( u_t == w and u_m <> w) => ( d i_m i_t d => d i_m+1 i_t d finish_run_of_model = Or([tau_it([i_m + 1, t(wait_transition)]), djiid([j, i_m + 1, i_t + 1, d])], [lambda_jia([j, i_t + 1, t(wait_transition)]), djiid([j, i_m + 1, i_t, d])], []) formulas.append(finish_run_of_model) # ( u_m == w and u_t <> w) => ( d i_m i_t d => d i_m i_t+1 d condition=[tau_it([i_m + 1, t(wait_transition)])] for st in silent_transitions: condition.append(tau_it([i_m + 1, t(st)])) finish_run_of_trace = Or( [lambda_jia([j, i_t + 1, t(wait_transition)]), djiid([j, i_m + 1, i_t + 1, d])], [ djiid([j, i_m, i_t + 1, d])], [ And([], condition,[]) ]) formulas.append(finish_run_of_trace) return formulas
def apply(net1, m01, mf1, net2, m02, mf2, size_of_run, d, silent_label=None): vars = vg.VariablesGenerator() #we=add_wait_net_end(net1,"wf") w1 = add_wait_net(net1, "wf") adapted_size_of_run = size_of_run * size_of_run + size_of_run pn1_formula, pn1_places, pn1_transitions, pn1_silent_transitions = petri_net_to_SAT( net1, m01, mf1, vars, adapted_size_of_run, reach_final=True, label_m=BOOLEAN_VAR_MARKING_PN_1, label_t=BOOLEAN_VAR_FIRING_TRANSITION_PN_1, silent_transition=silent_label, space_between_fired=1 + size_of_run) print("etape1") pn1_force_wait_transitions = force_wait_transition(vars, w1, pn1_transitions, adapted_size_of_run, size_of_run + 1) print("etape2") w2 = add_wait_net(net2, "wf") pn2_formula, pn2_places, pn2_transitions, pn2_silent_transitions = petri_net_to_SAT( net2, m02, mf2, vars, adapted_size_of_run, reach_final=True, label_m=BOOLEAN_VAR_MARKING_PN_2, label_t=BOOLEAN_VAR_FIRING_TRANSITION_PN_2, silent_transition=silent_label) dist_formulas = distanceNets( vars, adapted_size_of_run, vars.getFunction(BOOLEAN_VAR_FIRING_TRANSITION_PN_1), vars.getFunction(BOOLEAN_VAR_FIRING_TRANSITION_PN_2), pn1_transitions, pn2_transitions, w1, w2) print("etape3") maxDist_formulas = maxDistance(vars, vars.getFunction(BOOLEAN_VAR_DIFF1), vars.getFunction(BOOLEAN_VAR_DIFF2), d, adapted_size_of_run) #notTooManyW=numberOfWaitInRun(vars,size_of_run,vars.getFunction(BOOLEAN_VAR_FIRING_TRANSITION_PN_1),pn1_transitions,w1,we) print("etape4") from pm4py.visualization.petrinet import factory as vizu #vizu.apply(net2,m02,mf2).view() listOfForAll = vars.getAll(BOOLEAN_VAR_MARKING_PN_1) + vars.getAll( BOOLEAN_VAR_FIRING_TRANSITION_PN_1) listOfExist = vars.getAll(BOOLEAN_VAR_MARKING_PN_2) + vars.getAll( BOOLEAN_VAR_FIRING_TRANSITION_PN_2) + vars.getAll( BOOLEAN_VAR_DIFF1) + vars.getAll(BOOLEAN_VAR_DIFF2) full_formula = Or([], [], [ And([], [], [pn1_formula, pn1_force_wait_transitions]).negation(), And([], [], [dist_formulas, maxDist_formulas, pn2_formula]) ]) print("etape5") cnf = full_formula.operatorToCnf(vars.iterator) print("etape6") listOfExist += list(range(vars.iterator, full_formula.nbVars)) writeQDimacs(full_formula.nbVars, listOfForAll, listOfExist, cnf) print("mais voila") runCadet() positives, negatives = cadetOutputQDimacs() for var in positives: if vars.getVarName(var) != None and vars.getVarName(var).startswith( "tau1_ia"): print( vars.getVarName(var), pn1_transitions[int( vars.getVarName(var).split(", ")[1].split("]")[0])]) print("....") for var in negatives: if vars.getVarName(var) != None and vars.getVarName(var).startswith( "tau1_ia"): print( vars.getVarName(var), pn1_transitions[int( vars.getVarName(var).split(", ")[1].split("]")[0])])
def __createCentroids(self, m0, mf): ''' Create formula of the subnet centroids. There are one centroid per trace and transitions are affected to cluster. As the number of cluster is limited, centroid will naturally be joined : traces are clustered that way. Centroids finally are run of the model that allows alignments. When alignment are found, transitions go in clusters. Creates BOOLEAN_VAR_CHI_MARKINGS, BOOLEAN_VAR_CHI_TRANSITIONS, BOOLEAN_VAR_K_CONTAINS_T, BOOLEAN_VAR_J_IN_K boolean variables. This function has subfunctions because formula differ from normal petri nets (@see pnToFormula). :param m0 (Marking) ''' def in_cluster_of_j(tr, c_kt, j, chi_jk, nb_clusters): ''' Subfunction of __createCentroids. in_cluster_of_j function affects a transition to the cluster of the trace. :param tr (Transition) :param c_kt (function) : @see variablesGenerator.py, function c_kt(k,t) gets BOOLEAN_VAR_K_CONTAINS_T variables :param j (int) : index of the current trace :param chi_jk (function) : @see variablesGenerator.py, function chi_jk gets BOOLEAN_VAR_J_IN_K variables :param nb_clusters (int) : number of cluster :return: ''' # BOOLEAN_VAR_J_IN_K => BOOLEAN_VAR_K_CONTAINS_T return And([], [], [ Or([c_kt([k, tr])], [chi_jk([j, k])], []) for k in range(0, nb_clusters) ]) def is_transition_centroid(j, places, tr, i, m_ip): ''' This function verifies marking to fire transition of a centroid. :param j (int) : index of centroid :param places (list) : list of places :param tr (Transition) : transition that wants to fire :param i (int) : instant of firing :param m_ip (function) : @see @variablesGenerator.py function to get BOOLEAN_VAR_CHI_MARKINGS variables ''' formulas = [] prePlaces = [a.source for a in tr.in_arcs] postPlaces = [a.target for a in tr.out_arcs] # token game for p in places: if p in prePlaces and p in postPlaces: formulas.append( And([ m_ip([j, i, places.index(p)]), m_ip([j, i - 1, places.index(p)]) ], [], [])) elif p in prePlaces and p not in postPlaces: formulas.append( And([m_ip([j, i - 1, places.index(p)])], [m_ip([j, i, places.index(p)])], [])) elif p not in prePlaces and p in postPlaces: formulas.append( And([m_ip([j, i, places.index(p)])], [m_ip([j, i - 1, places.index(p)])], [])) elif p not in prePlaces and p not in postPlaces: formulas.append( Or([], [], [ And([ m_ip([j, i, places.index(p)]), m_ip([j, i - 1, places.index(p)]) ], [], []), And([], [ m_ip([j, i, places.index(p)]), m_ip([j, i - 1, places.index(p)]) ], []) ])) return And([], [], formulas) def is_action_centroid(j, places, transitions, i, m_jip, tau_jip, c_kt, chi_jk, nb_clusters): ''' @see pnToFormula.py, is_action_centroid says if transition is fired. :param j (int) : index of trace :param places (list) : places of the petri net, indexes are important :param transitions (list) : transitions of the petri net, indexes are important :param i (int) : instant in the run of the centroid :param m_jip (function) : @see variablesGenerator.py, function chi_jk gets BOOLEAN_VAR_CHI_MARKINGS variables :param tau_jip (function) : @see variablesGenerator.py, function chi_jk gets BOOLEAN_VAR_CHI_TRANSITIONS variables :param c_kt (function) : @see variablesGenerator.py, function chi_jk gets BOOLEAN_VAR_K_CONTAINS_T variables :param chi_jk (function) : @see variablesGenerator.py, function chi_jk gets BOOLEAN_VAR_J_IN_K variables :param nb_clusters (int) : number of cluster :return: ''' # a transition fires and only one aTransitionPerInstant = [ And([tau_jip([j, i, t])], [ tau_jip([j, i, t2]) for t2 in range(len(transitions)) if t != t2 ], []) for t in range(len(transitions)) ] formulas = [Or([], [], aTransitionPerInstant)] # runs is_transition for the fired transition indexOfTraceWait = transitions.index(self.__wait_transition_trace) for t in range(len(transitions)): # WAIT_TRANSITION_TRACE is forbidden for centroid if t == indexOfTraceWait: formulas.append(And([], [tau_jip([j, i, t])], [])) else: formulas.append( Or([], [tau_jip([j, i, t])], [ And([], [], [ is_transition_centroid( j, places, transitions[t], i, m_jip), in_cluster_of_j(t, c_kt, j, chi_jk, nb_clusters) ]) ])) return And([], [], formulas) def is_run_centroid(j, size_of_run, m0, mf, m_jip, tau_jip, c_kt, chi_jk, nb_clusters, transitions, places): ''' Initialization of centroids. There is a run per trace. This run represents alignment of the trace to the model. If trace is clusterised then transition of it run are containing in the centroid of its cluster. :param j (int) : index of trace :param size_of_run (int) : maximal size of the run, prefix :param m0 (Marking) : initial marking :param m_jip (function) : @see variablesGenerator.py, function chi_jk gets BOOLEAN_VAR_CHI_MARKINGS variables :param tau_jip (function) : @see variablesGenerator.py, function chi_jk gets BOOLEAN_VAR_CHI_TRANSITIONS variables :param c_kt (function) : @see variablesGenerator.py, function chi_jk gets BOOLEAN_VAR_K_CONTAINS_T variables :param chi_jk (function) : @see variablesGenerator.py, function chi_jk gets BOOLEAN_VAR_J_IN_K variables :param nb_clusters (int) : number of cluster :param transitions (list) : list of Transitions :param places (list) : list of Places :return: ''' positives = [m_jip([j, 0, places.index(m)]) for m in m0] for m in mf: positives.append(m_jip([j, size_of_run, places.index(m)])) negatives = [ m_jip([j, 0, places.index(m)]) for m in places if m not in m0 ] formulas = [ is_action_centroid(j, places, transitions, i, m_jip, tau_jip, c_kt, chi_jk, nb_clusters) for i in range(1, size_of_run + 1) ] run_of_pn = And(positives, negatives, formulas) return run_of_pn # ..................................................................................... # here starts __createCentroids function centroidsFormulas = [] for j in range(0, len(self.__traces)): centroidOfJ = is_run_centroid( j, self.__size_of_run, m0, mf, self.__vars.getFunction(BOOLEAN_VAR_CHI_MARKINGS), self.__vars.getFunction(BOOLEAN_VAR_CHI_TRANSITIONS), self.__vars.getFunction(BOOLEAN_VAR_K_CONTAINS_T), self.__vars.getFunction(BOOLEAN_VAR_J_IN_K), self.__nb_clusters, self.__transitions, self.__places) centroidIfClusterised = Or( [], [self.__vars.get(BOOLEAN_VAR_J_CLUSTERISED, [j])], [centroidOfJ]) centroidsFormulas.append(centroidIfClusterised) return centroidsFormulas