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 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 __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 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 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 is_run_for_j(j, size_of_run, places, pn_transitions, trace_transitions, m0, mf, m_ip, tau_it, reach_final): ''' The is_run_for_j method creates run of centroid of trace j, i. e., alignment. :param j (int) : index of trace :param size_of_run (int): maximal size of the run :param places (list) : list of Places :param pn_transitions (list) : transitions of the model 'cause needed... :param trace_transitions (list) : transitions of the trace :param m0 (marking) : initial marking of trace :param mf (Marking) : final marking of trace :param m_ip (function) : marking boolean variables of the trace :param tau_it (function) : transitions boolean variables of the trace, @see variablesGenerator. :param reach_final (bool) : true or false to reach final marking :return: ''' positives = [m_ip([j, 0, places.index(m)]) for m in m0] if reach_final: [ positives.append(m_ip([j, size_of_run, places.index(m)])) for m in mf ] negatives = [ m_ip([j, 0, places.index(m)]) for m in places if m not in m0 ] formulas = [ is_action_for_j(j, places, pn_transitions, trace_transitions, i, m_ip, tau_it) for i in range(1, size_of_run + 1) ] run_of_pn = And(positives, negatives, formulas) return run_of_pn
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
def is_run(size_of_run, places, transitions, m0, mf, m_ip, tau_it, reach_final, space_between_fired): ''' The is_run method allows one to create the boolean paths of the petri net. :param size_of_run (int): maximal size of the run :param places (list) : :param transitions (list) : :param m0 (marking) : initial marking :param m_ip (marking) : final marking :param tau_it (function) : function to get the number of the boolean variables, see variablesGenerator. :param reach_final (bool) : true or false to reach final marking :return: ''' positives = [m_ip([0, places.index(m)]) for m in m0] if reach_final: positives += [m_ip([size_of_run, places.index(m)]) for m in mf] negatives = [m_ip([0, places.index(m)]) for m in places if m not in m0] formulas = [ is_action(places, transitions, m0, i, m_ip, tau_it, space_between_fired) for i in range(space_between_fired, size_of_run + 1, space_between_fired) ] run_of_pn = And(positives, negatives, formulas) return run_of_pn
def for_hamming_distance_aux_supd_anti(variables,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(variables.get(BOOLEAN_VAR_HAMMING_DISTANCE, [j, instant])) and_sub_instants.append(And(instants_to_combine,[],[])) not_d_or_and_diff.append(Or([], [variables.get(BOOLEAN_VAR_HAMMING_SUP_AUX, [j, d])], 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 __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 __createSATformula(self, pn, m0, mf, max_d, max_t, traces_xes, nbTraces): ''' This function creates and solve the SAT formula of the clustering problem. :param pn (Petrinet) :param m0 (Marking) :param mf (Marking) :param max_d (int) :param max_t (int) :param traces_xes (Log) ''' # this object creates variable numbers of the SAT formula self.__vars = VariablesGenerator() # formula version of data event log log_to_PN_w_formula, self.__traces = log_to_Petri_with_w( traces_xes, self.__transitions, self.__vars, self.__size_of_run, self.__wait_transition_trace, self.__wait_transition_model, label_l=BOOLEAN_VAR_TRACES_ACTIONS, max_nbTraces=nbTraces) # creates the boolean variables for the next formulas self.__createBooleanVariables() # formula of centroids centroidsFormulasList = self.__createCentroids(m0, mf) # formula that describes maximal distance diffTracesCentroids = self.__getDiffTracesCentroids( self.__vars.getFunction(BOOLEAN_VAR_CHI_TRANSITIONS), self.__vars.getFunction(BOOLEAN_VAR_DIFF_l), self.__vars.getFunction(BOOLEAN_VAR_DIFF_m), self.__vars.getFunction(BOOLEAN_VAR_TRACES_ACTIONS)) # formula that create BOOLEAN_VAR_COMMON_T variables listOfCommonTransitions = self.__commonTransitions( self.__vars.getFunction(BOOLEAN_VAR_COMMON_T), self.__vars.getFunction(BOOLEAN_VAR_K_CONTAINS_T)) # formula that describes that a trace belongs to at most one cluster aClusterMax = self.__tracesInAClusterOnly( self.__vars.getFunction(BOOLEAN_VAR_J_CLUSTERISED), self.__vars.getFunction(BOOLEAN_VAR_J_IN_K)) # concat the formula full_formula = And([], [], log_to_PN_w_formula + centroidsFormulasList + diffTracesCentroids + listOfCommonTransitions + aClusterMax) # formula to cnf cnf = full_formula.operatorToCnf(self.__vars.iterator) # CNF is completed with minimisation and solved self.__createWCNFWithMinimization(cnf)
def force_wait_transition(vars, w1, pn1_transitions, adapted_size_of_run, space_between_fired): pos = [] neg = [] for i in range(0, adapted_size_of_run + 1): if i % space_between_fired != 0: for t in pn1_transitions: if t != w1: neg.append( vars.get(BOOLEAN_VAR_FIRING_TRANSITION_PN_1, [i, pn1_transitions.index(t)])) else: pos.append( vars.get(BOOLEAN_VAR_FIRING_TRANSITION_PN_1, [i, pn1_transitions.index(t)])) return And(pos, neg, [])
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 log_to_SAT(traces_xes, transitions, variablesGenerator, size_of_run, wait_transition, label_l=BOOLEAN_VAR_TRACES_ACTIONS, max_nbTraces=None): ''' This method returns the formulas of the Log. :param traces_xes: :param transitions (list) :param variablesGenerator (variablesGenerator) : to add the new boolean variables :param size_of_run (int) : to complete smaller words with "wait" transitions :param wait_transition (transition) : the "wait" transition :param label_l (string) : name of the boolean variables of the log :return: ''' traces_multiples = project_traces(traces_xes) #traces=list(np.unique(traces_multiples)) traces = traces_multiples traces = traces[:max_nbTraces] if max_nbTraces != None else traces variablesGenerator.add(label_l, [(0, len(traces)), (1, size_of_run + 1), (0, len(transitions))]) lambda_jia = variablesGenerator.getFunction(label_l) positives = [] negatives = [] for j in range(0, len(traces)): for i in range(1, size_of_run + 1): if len(traces[j]) < (i): positives.append( lambda_jia([j, i, transitions.index(wait_transition)])) for a in transitions: if a != wait_transition: negatives.append( lambda_jia([j, i, transitions.index(a)])) else: for a in transitions: if str(a) == traces[j][i - 1]: letterFound = True positives.append( lambda_jia([j, i, transitions.index(a)])) else: negatives.append( lambda_jia([j, i, transitions.index(a)])) return And(positives, negatives, []), traces
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 __createWncf(self, initialisationFormulas, distanceFormula, artefactForMinimization): ''' This method creates the wncf formulas with the weighted variables depending on the distance and artefact. :param initialisationFormulas: @see __artefactsInitialisation :param distanceFormula: @see __compute_distance :param artefactForMinimization: MULTI_ALIGNMENT or ANTI_ALIGNMENT or EXACT_ALIGNMENT :return: ''' formulas = initialisationFormulas + distanceFormula + self.__sup_to_minimize( artefactForMinimization) full_formula = And([], [], formulas) cnf = full_formula.operatorToCnf(self.__vars.iterator) wcnf = WCNF() wcnf.extend(cnf) wcnf = self.__createWeights(wcnf, artefactForMinimization) self.__formula_time = time.time() return wcnf
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 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 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])])