def __search(sync_net, ini, fin, cost_function, skip, ret_tuple_as_trans_desc=False, max_align_time_trace=sys.maxsize): start_time = time.time() decorate_transitions_prepostset(sync_net) decorate_places_preset_trans(sync_net) incidence_matrix = inc_mat_construct(sync_net) ini_vec, fin_vec, cost_vec = utils.__vectorize_initial_final_cost(incidence_matrix, ini, fin, cost_function) closed = set() a_matrix = np.asmatrix(incidence_matrix.a_matrix).astype(np.float64) g_matrix = -np.eye(len(sync_net.transitions)) h_cvx = np.matrix(np.zeros(len(sync_net.transitions))).transpose() cost_vec = [x * 1.0 for x in cost_vec] use_cvxopt = False if lp_solver.DEFAULT_LP_SOLVER_VARIANT == lp_solver.CVXOPT_SOLVER_CUSTOM_ALIGN or lp_solver.DEFAULT_LP_SOLVER_VARIANT == lp_solver.CVXOPT_SOLVER_CUSTOM_ALIGN_ILP: use_cvxopt = True if use_cvxopt: # not available in the latest version of PM4Py from cvxopt import matrix a_matrix = matrix(a_matrix) g_matrix = matrix(g_matrix) h_cvx = matrix(h_cvx) cost_vec = matrix(cost_vec) h, x = utils.__compute_exact_heuristic_new_version(sync_net, a_matrix, h_cvx, g_matrix, cost_vec, incidence_matrix, ini, fin_vec, lp_solver.DEFAULT_LP_SOLVER_VARIANT, use_cvxopt=use_cvxopt) ini_state = utils.SearchTuple(0 + h, 0, h, ini, None, None, x, True) open_set = [ini_state] heapq.heapify(open_set) visited = 0 queued = 0 traversed = 0 trans_empty_preset = set(t for t in sync_net.transitions if len(t.in_arcs) == 0) while not len(open_set) == 0: if (time.time() - start_time) > max_align_time_trace: return None curr = heapq.heappop(open_set) current_marking = curr.m # 11/10/2019 (optimization Y, that was optimization X, # but with the good reasons this way): avoid checking markings in the cycle using # the __get_alt function, but check them 'on the road' already_closed = current_marking in closed if already_closed: continue while not curr.trust: h, x = utils.__compute_exact_heuristic_new_version(sync_net, a_matrix, h_cvx, g_matrix, cost_vec, incidence_matrix, curr.m, fin_vec, lp_solver.DEFAULT_LP_SOLVER_VARIANT, use_cvxopt=use_cvxopt) # 11/10/19: shall not a state for which we compute the exact heuristics be # by nature a trusted solution? tp = utils.SearchTuple(curr.g + h, curr.g, h, curr.m, curr.p, curr.t, x, True) # 11/10/2019 (optimization ZA) heappushpop is slightly more efficient than pushing # and popping separately curr = heapq.heappushpop(open_set, tp) current_marking = curr.m # max allowed heuristics value (27/10/2019, due to the numerical instability of some of our solvers) if curr.h > lp_solver.MAX_ALLOWED_HEURISTICS: continue # 12/10/2019: do it again, since the marking could be changed already_closed = current_marking in closed if already_closed: continue # 12/10/2019: the current marking can be equal to the final marking only if the heuristics # (underestimation of the remaining cost) is 0. Low-hanging fruits if curr.h < 0.01: if current_marking == fin: return utils.__reconstruct_alignment(curr, visited, queued, traversed, ret_tuple_as_trans_desc=ret_tuple_as_trans_desc) closed.add(current_marking) visited += 1 possible_enabling_transitions = copy(trans_empty_preset) for p in current_marking: for t in p.ass_trans: possible_enabling_transitions.add(t) enabled_trans = [t for t in possible_enabling_transitions if t.sub_marking <= current_marking] trans_to_visit_with_cost = [(t, cost_function[t]) for t in enabled_trans if not ( t is not None and utils.__is_log_move(t, skip) and utils.__is_model_move(t, skip))] for t, cost in trans_to_visit_with_cost: traversed += 1 new_marking = utils.add_markings(current_marking, t.add_marking) if new_marking in closed: continue g = curr.g + cost queued += 1 h, x = utils.__derive_heuristic(incidence_matrix, cost_vec, curr.x, t, curr.h) trustable = utils.__trust_solution(x) new_f = g + h tp = utils.SearchTuple(new_f, g, h, new_marking, curr, t, x, trustable) heapq.heappush(open_set, tp)
def __search(sync_net, ini, fin, stop, cost_function, skip): decorate_transitions_prepostset(sync_net) decorate_places_preset_trans(sync_net) incidence_matrix = petri.incidence_matrix.construct(sync_net) ini_vec, fin_vec, cost_vec = utils.__vectorize_initial_final_cost( incidence_matrix, ini, fin, cost_function) closed = set() ini_state = utils.SearchTuple(0, 0, 0, ini, None, None, None, True) open_set = [ini_state] heapq.heapify(open_set) visited = 0 queued = 0 traversed = 0 # return all the prefix markings of the optimal alignments as set ret_markings = None # keep track of the optimal cost of an alignment (to trim search when needed) optimal_cost = None while not len(open_set) == 0: curr = heapq.heappop(open_set) current_marking = curr.m # trim alignments when we already reached an optimal alignment and the # current cost is greater than the optimal cost if optimal_cost is not None and curr.f > optimal_cost: break already_closed = current_marking in closed if already_closed: continue if stop <= current_marking: # add the current marking to the set # of returned markings if ret_markings is None: ret_markings = set() ret_markings.add(current_marking) # close the marking closed.add(current_marking) # set the optimal cost optimal_cost = curr.f continue closed.add(current_marking) visited += 1 possible_enabling_transitions = set() for p in current_marking: for t in p.ass_trans: possible_enabling_transitions.add(t) enabled_trans = [ t for t in possible_enabling_transitions if t.sub_marking <= current_marking ] trans_to_visit_with_cost = [ (t, cost_function[t]) for t in enabled_trans if not (t is None or utils.__is_log_move(t, skip) or ( utils.__is_model_move(t, skip) and not t.label[1] is None)) ] for t, cost in trans_to_visit_with_cost: traversed += 1 new_marking = utils.add_markings(current_marking, t.add_marking) if new_marking in closed: continue g = curr.g + cost queued += 1 new_f = g tp = utils.SearchTuple(new_f, g, 0, new_marking, curr, t, None, True) heapq.heappush(open_set, tp) return ret_markings