示例#1
0
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)

    closed = set()

    ini_state = utils.DijkstraSearchTuple(0, ini, None, None, 0)
    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
        already_closed = current_marking in closed
        if already_closed:
            continue

        if current_marking == fin:
            # from pympler.asizeof import asizeof
            # from pm4py.util import measurements
            # measurements.Measurements.ALIGN_TIME.append(asizeof(open_set))
            return utils.__reconstruct_alignment(curr, visited, queued, traversed,
                                                 ret_tuple_as_trans_desc=ret_tuple_as_trans_desc)

        closed.add(current_marking)
        visited += 1

        enabled_trans = copy(trans_empty_preset)
        for p in current_marking:
            for t in p.ass_trans:
                if t.sub_marking <= current_marking:
                    enabled_trans.add(t)

        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

            queued += 1

            tp = utils.DijkstraSearchTuple(curr.g + cost, new_marking, curr, t, curr.l + 1)

            heapq.heappush(open_set, tp)
示例#2
0
    def _expand_from_current_marking(self, curr, cost_vec, closed):
        """
        Expand all subsequent markings from current marking.
        """

        # get subsequent firing transitions
        enabled_trans1 = {}
        for p in curr.m:
            for t in p.ass_trans:
                if t.sub_marking <= curr.m:
                    enabled_trans1[t] = self.incidence_matrix.transitions[t]
                    enabled_trans1 = dict(sorted(enabled_trans1.items(), key=lambda item: item[1]))
        enabled_trans = enabled_trans1.keys()
        trans_to_visit_with_cost = [(t, self.cost_function[t]) for t in enabled_trans if not (
                t is not None and is_log_move(t, '>>') and is_model_move(t, '>>'))]
        for t, cost in trans_to_visit_with_cost:
            # compute the new g score of the subsequent marking reached if t would be fired
            new_g = curr.g + cost
            new_m = add_markings(curr.m, t.add_marking)
            self.traversed_arc += 1
            # subsequent marking is fresh, compute the f score of this path and add it to open set
            if new_m in closed:
                # the heuristic is not consistent, th us smaller g than closed could happen
                if closed[new_m] > new_g:
                    del closed[new_m]
                    self.order += 1
                    new_h, new_x, trustable = derive_heuristic(cost_vec, curr.x, self.incidence_matrix.transitions[t], curr.h)
                    marking_to_reopen = NormalMarking(new_g + new_h, new_g, new_h, new_m, curr, t, new_x, trustable,
                                       self.order)
                    if trustable and t.label[0] != ">>":
                        curr_max = get_max_events(marking_to_reopen)
                        if curr_max > self.max_rank:
                            self.max_rank = curr_max
                    start_time = timeit.default_timer()
                    self.open_set.heap_insert(marking_to_reopen)
                    self.queue_time += timeit.default_timer() - start_time
                    self.num_insert += 1
            else:
                start_time = timeit.default_timer()
                m_in_open = self.open_set.heap_find(new_m)
                self.queue_time += timeit.default_timer() - start_time
                # subsequent marking is already in open set
                if m_in_open:
                    start_time = timeit.default_timer()
                    marking_to_explore = self.open_set.heap_get(new_m)
                    self.queue_time += timeit.default_timer() - start_time
                    if new_g < marking_to_explore.g:
                        marking_to_explore.p, marking_to_explore.t, marking_to_explore.g = curr, t, new_g
                        marking_to_explore.h, marking_to_explore.x, marking_to_explore.trust = \
                            derive_heuristic(cost_vec, curr.x, self.incidence_matrix.transitions[t], curr.h)
                        marking_to_explore.f = marking_to_explore.g + marking_to_explore.h
                        start_time = timeit.default_timer()
                        self.open_set.heap_update(marking_to_explore)
                        self.queue_time += timeit.default_timer() - start_time
                        self.num_update += 1
                        if t.label[0] != ">>":
                            curr_max = get_max_events(marking_to_explore)
                            if curr_max > self.max_rank:
                                self.max_rank = curr_max
                    # subsequent marking has equal path, but the heuristic change from infeasible to feasible
                    elif not marking_to_explore.trust:
                        new_h, new_x, trustable = derive_heuristic(cost_vec, curr.x, self.incidence_matrix.transitions[t], curr.h)
                        if trustable:
                            marking_to_explore.h = new_h
                            marking_to_explore.f = new_h + marking_to_explore.g
                            marking_to_explore.trustable = True
                            marking_to_explore.x = new_x
                            start_time = timeit.default_timer()
                            self.open_set.heap_update(marking_to_explore)
                            self.queue_time += timeit.default_timer() - start_time
                            self.num_update += 1
                            if t.label[0] != ">>":
                                curr_max = get_max_events(marking_to_explore)
                                if curr_max > self.max_rank:
                                    self.max_rank = curr_max
                else:
                    self.order += 1
                    new_h, new_x, trustable = derive_heuristic(cost_vec, curr.x, self.incidence_matrix.transitions[t], curr.h)
                    new_marking_to_explore = NormalMarking(new_g + new_h, new_g, new_h, new_m, curr, t, new_x, trustable,
                                       self.order)
                    if trustable and t.label[0] != ">>":
                        curr_max = get_max_events(new_marking_to_explore)
                        if curr_max > self.max_rank:
                            self.max_rank = curr_max
                    start_time = timeit.default_timer()
                    self.open_set.heap_insert(new_marking_to_explore)
                    self.queue_time += timeit.default_timer() - start_time
                    self.num_insert += 1
示例#3
0
def __search(net, ini, fin, variants, exponent=2, marking_limit=1000):
    '''
    This function is the A* algorithm for multi-alignments.
    '''

    decorate_transitions_prepostset(net)
    decorate_places_preset_trans(net)

    mymemory = {}
    closed = {}

    ini_state = utils.DijkstraSearchTupleForAntiAndMulti(0, ini, [])
    open_set = [ini_state]
    heapq.heapify(open_set)
    visited = 0
    queued = 0
    traversed = 0
    best, sizeAA = None, None

    distanceTime = 0
    trans_empty_preset = set(t for t in net.transitions if len(t.in_arcs) == 0)

    def costfunction(t, curr_ma, withFrac=True):
        '''
        Compute the maximal distance of the current multi-alignment + t and the variants.
        :param t: transition that we want to add in the current multi-alignment
        :param curr_ma: current prefix of multi-alignment
        :param withFrac: the fraction that prevents the best suffice, this parameter is false at the end of the algorithm.
        '''
        if t is None:
            str_aa = [a.label for a in curr_ma]
        elif len(curr_ma) == 0:
            str_aa = [t.label]
        else:
            str_aa = [a.label for a in curr_ma] + [t.label]

        all = []
        for v in variants:
            if str(v + str_aa) not in mymemory.keys() or not withFrac:
                size_of_alignment, distance = discountedEditDistance(
                    str_aa, v, exponent)
                if withFrac:
                    mymemory[str(v + str_aa)] = distance - (
                        exponent**(-len(str_aa) + 1) - exponent**
                        (-(len(str_aa) + len(v)))) / (exponent - 1)
                else:
                    mymemory[str(v + str_aa)] = distance
            all.append(mymemory[str(v + str_aa)])
        return max(all)

    # ----------------------------
    # a* algorithm starts here
    while not len(open_set) == 0:
        curr = heapq.heappop(open_set)

        if best and best.g < curr.g:
            continue

        if curr.m == fin:
            curr.g = costfunction(None, curr.r, withFrac=False)
            # in case there only one run
            if not best and (len(heapq.nsmallest(1, open_set)) == 0
                             or heapq.nsmallest(1, open_set)[0].g > curr.g):
                return {
                    'multi-alignment': curr.r,
                    'cost': curr.g,
                    'visited_states': visited,
                    'queued_states': queued,
                    'traversed_arcs': traversed,
                    "max_distance_to_log": getMaxDist(curr.r, variants)
                }

            # other runs might be interesting
            elif not best or (best and best.g > curr.g):
                best = curr
            continue

        closed[curr.m] = 1 if (
            curr.m) not in closed.keys() else closed[curr.m] + 1

        visited += 1

        possible_enabling_transitions = copy(trans_empty_preset)
        for p in curr.m:
            for t in p.ass_trans:
                possible_enabling_transitions.add(t)

        enabled_trans = [
            t for t in possible_enabling_transitions if t.sub_marking <= curr.m
        ]
        trans_to_visit_with_cost = [(t, costfunction(t, curr.r, withFrac=True))
                                    for t in enabled_trans if t is not None]

        for t, cost in trans_to_visit_with_cost:
            traversed += 1
            new_marking = utils.add_markings(curr.m, t.add_marking)

            if ((new_marking) in closed.keys()
                    and closed[new_marking] > marking_limit) or cost == curr.g:
                continue
            queued += 1
            tp = utils.DijkstraSearchTupleForAntiAndMulti(
                cost, new_marking, curr.r + [t])
            heapq.heappush(open_set, tp)

    return {
        'multi-alignment': best.r,
        'cost': best.g,
        'visited_states': visited,
        'queued_states': queued,
        'traversed_arcs': traversed,
        "max_distance_to_log": getMaxDist(best.r, variants)
    }
示例#4
0
文件: utils.py 项目: pm4py/pm4py-core
def __search(sync_net, ini, fin, stop, cost_function, skip):
    decorate_transitions_prepostset(sync_net)
    decorate_places_preset_trans(sync_net)

    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

        enabled_trans = set()
        for p in current_marking:
            for t in p.ass_trans:
                if t.sub_marking <= current_marking:
                    enabled_trans.add(t)

        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
示例#5
0
    def _expand_from_current_marking(self, curr, cost_vec, closed):
        """
        Expand all subsequent markings from current marking.
        """

        enabled_trans1 = {}
        for p in curr.m:
            for t in p.ass_trans:
                if t.sub_marking <= curr.m:
                    enabled_trans1[t] = self.incidence_matrix.transitions[t]
                    enabled_trans1 = dict(
                        sorted(enabled_trans1.items(),
                               key=lambda item: item[1]))
        enabled_trans = enabled_trans1.keys()
        trans_to_visit_with_cost = [
            (t, self.cost_function[t]) for t in enabled_trans
            if not (t is not None and is_log_move(t, '>>')
                    and is_model_move(t, '>>'))
        ]

        for t, cost in trans_to_visit_with_cost:
            t_idx = self.incidence_matrix.transitions[t]
            # compute the new g score of the subsequent marking reached if t would be fired
            new_g = curr.g + cost
            new_m = add_markings(curr.m, t.add_marking)
            self.traversed_arc += 1
            # subsequent marking is fresh, compute the f score of this path and add it to open set
            if new_m in closed:
                marking_to_explore = closed[new_m]
                # reach this marking with shorter path, abandon all longer paths
                if marking_to_explore.g > new_g:
                    t_idx = self.incidence_matrix.transitions[t]
                    del closed[new_m]
                    marking_to_explore.p, marking_to_explore.t, marking_to_explore.g = curr, t, new_g
                    # update previous transition list and abandon path before
                    new_parikh_vec_lst = get_parikh_vec_lst(
                        curr.parikh_vec_lst, t_idx)
                    marking_to_explore.parikh_vec_lst = new_parikh_vec_lst
                    new_h, new_x, trustable = derive_multi_heuristic(
                        cost_vec, curr.x, t_idx, curr.h)
                    marking_to_explore.h = new_h
                    marking_to_explore.x = new_x
                    marking_to_explore.trust = trustable
                    marking_to_explore.f = marking_to_explore.g + new_h
                    start_time = timeit.default_timer()
                    self.queue += timeit.default_timer() - start_time
                    self.update += 1
                    if trustable or not self.split_open_set_flag:
                        if trustable:
                            marking_to_explore.heuristic_priority = curr.heuristic_priority
                            self.max_rank = check_max_event(
                                marking_to_explore, self.max_rank, t)
                        start_time = timeit.default_timer()
                        self.open_set.heap_insert(marking_to_explore)
                        self.queue += timeit.default_timer() - start_time
                        self.insertion += 1
                    else:
                        self.infeasible_set[new_m] = marking_to_explore
                elif marking_to_explore.g == new_g:
                    # update previous paths
                    temp_parikh_vec_lst = get_parikh_vec_lst(
                        curr.parikh_vec_lst, t_idx)
                    new_parikh_vec_lst, update_flag = update_parikh_vec_lst(
                        temp_parikh_vec_lst, marking_to_explore.parikh_vec_lst)
                    # if new paths found
                    if update_flag:
                        del closed[new_m]
                        marking_to_explore.parikh_vec_lst = new_parikh_vec_lst
                        # compute all possible solution vector
                        new_h, new_x, trustable = derive_multi_heuristic(
                            cost_vec, curr.x, t_idx, curr.h)
                        # if the h is still feasible, keep it in open set, otherwise put into infeasible set
                        if trustable or not self.split_open_set_flag:
                            if trustable:
                                if curr.heuristic_priority > marking_to_explore.heuristic_priority:
                                    marking_to_explore.heuristic_priority = curr.heuristic_priority
                                    marking_to_explore.x = new_x
                                else:
                                    marking_to_explore.x = concatenate_two_sol(
                                        marking_to_explore.x, new_x)
                            marking_to_explore.h = min(marking_to_explore.h,
                                                       new_h)
                            marking_to_explore.f = marking_to_explore.g + marking_to_explore.h
                            start_time = timeit.default_timer()
                            self.open_set.heap_insert(marking_to_explore)
                            self.queue += timeit.default_timer() - start_time
                            self.update += 1
                        else:
                            self.infeasible_set[new_m] = marking_to_explore

            else:
                # check if m is in feasible set
                m_in_open_flag = self.open_set.heap_find(new_m)
                # check if m is in infeasible set
                m_in_infeasible_set = self.split_open_set_flag and new_m in self.infeasible_set
                # split open set into two: set containing feasible markings and set containing infeasible markings
                if self.split_open_set_flag:
                    # if this marking is already in open set (feasible set)
                    if m_in_open_flag:
                        start_time = timeit.default_timer()
                        marking_to_explore = self.open_set.heap_get(new_m)
                        self.queue += timeit.default_timer() - start_time

                        # reach this marking with shorter path, abandon all longer paths
                        if new_g < marking_to_explore.g:
                            marking_to_explore.p, marking_to_explore.t, marking_to_explore.g = curr, t, new_g
                            # update previous transition list
                            marking_to_explore.parikh_vec_lst = get_parikh_vec_lst(
                                curr.parikh_vec_lst, t_idx)
                            # get heuristic and solution vector
                            new_h, new_x, trustable = derive_multi_heuristic(
                                cost_vec, curr.x, t_idx, curr.h)
                            marking_to_explore.h = new_h
                            marking_to_explore.x = new_x
                            marking_to_explore.trust = trustable
                            marking_to_explore.f = marking_to_explore.g + marking_to_explore.h
                            # if the h is still feasible, keep it in open set, otherwise put into infeasible set
                            if trustable:
                                start_time = timeit.default_timer()
                                self.open_set.heap_update(marking_to_explore)
                                self.queue += timeit.default_timer(
                                ) - start_time
                                self.update += 1
                            else:
                                self.open_set.heap_remove(marking_to_explore)
                                self.infeasible_set[new_m] = marking_to_explore
                        # if found a path with equal length, need to propagate the new path
                        elif new_g == marking_to_explore.g:
                            # update previous transition list
                            temp_parikh_vec_lst = get_parikh_vec_lst(
                                curr.parikh_vec_lst, t_idx)
                            new_parikh_vec_lst, update_flag = update_parikh_vec_lst(
                                temp_parikh_vec_lst,
                                marking_to_explore.parikh_vec_lst)
                            # if new paths found
                            if update_flag:
                                marking_to_explore.parikh_vec_lst = new_parikh_vec_lst
                                # compute all possible solution vector
                                new_h, new_x, trustable = derive_multi_heuristic(
                                    cost_vec, curr.x, t_idx, curr.h)
                                # if the h is still feasible, keep it in open set, otherwise put into infeasible set
                                if trustable:
                                    marking_to_explore.h = min(
                                        marking_to_explore.h, new_h)
                                    marking_to_explore.x = concatenate_two_sol(
                                        marking_to_explore.x, new_x)
                                    marking_to_explore.f = marking_to_explore.g + marking_to_explore.h
                                    start_time = timeit.default_timer()
                                    self.open_set.heap_update(
                                        marking_to_explore)
                                    self.queue += timeit.default_timer(
                                    ) - start_time
                                    self.update += 1
                    # subsequent marking is not in open set but in infeasible set
                    elif m_in_infeasible_set:
                        marking_to_explore = self.infeasible_set[new_m]
                        # if paths are updated
                        if new_g < marking_to_explore.g:
                            marking_to_explore.p, marking_to_explore.t, marking_to_explore.g = curr, t, new_g
                            # update previous transition list
                            marking_to_explore.parikh_vec_lst = get_parikh_vec_lst(
                                curr.parikh_vec_lst, t_idx)
                            new_h, new_x, trustable = derive_multi_heuristic(
                                cost_vec, curr.x, t_idx, curr.h)
                            marking_to_explore.h = new_h
                            marking_to_explore.x = new_x
                            marking_to_explore.trust = trustable
                            marking_to_explore.f = marking_to_explore.g + marking_to_explore.h
                            if trustable:
                                # remove from infeasible set
                                del self.infeasible_set[new_m]
                                start_time = timeit.default_timer()
                                self.open_set.heap_insert(marking_to_explore)
                                self.queue += timeit.default_timer(
                                ) - start_time
                                self.update += 1
                                self.insertion += 1
                            else:
                                self.infeasible_set[new_m] = marking_to_explore
                        elif new_g == marking_to_explore.g:
                            # update previous transition list
                            temp_parikh_vec_lst = get_parikh_vec_lst(
                                curr.parikh_vec_lst, t_idx)
                            new_parikh_vec_lst, update_flag = update_parikh_vec_lst(
                                temp_parikh_vec_lst,
                                marking_to_explore.parikh_vec_lst)
                            # reach the marking with a different paths but has same g-value
                            if update_flag:
                                # compute all possible solution vector
                                marking_to_explore.parikh_vec_lst = new_parikh_vec_lst
                                new_h, new_x, trustable = derive_multi_heuristic(
                                    cost_vec, curr.x, t_idx, curr.h)
                                # if the h is still feasible, keep it in open set, otherwise put into infeasible set
                                if trustable:
                                    marking_to_explore.h = new_h
                                    marking_to_explore.x = new_x
                                    marking_to_explore.trust = True
                                    marking_to_explore.f = marking_to_explore.g + marking_to_explore.h
                                    # remove from infeasible set
                                    del self.infeasible_set[new_m]
                                    start_time = timeit.default_timer()
                                    self.open_set.heap_insert(
                                        marking_to_explore)
                                    self.queue += timeit.default_timer(
                                    ) - start_time
                                    self.insertion += 1
                                else:
                                    marking_to_explore.h = min(
                                        marking_to_explore.h, new_h)
                                    marking_to_explore.f = marking_to_explore.g + marking_to_explore.h
                                    self.infeasible_set[
                                        new_m] = marking_to_explore
                    # explore marking for the first time, namely, neither in open set or infeasible set
                    else:
                        # update previous transition list
                        new_parikh_vec_lst = get_parikh_vec_lst(
                            curr.parikh_vec_lst, t_idx)
                        t_idx = self.incidence_matrix.transitions[t]
                        new_h, new_x, trustable = derive_multi_heuristic(
                            cost_vec, curr.x, t_idx, curr.h)
                        # if the h is still feasible, keep it in open set, otherwise put into infeasible set
                        if trustable:
                            tp = CacheReopenMarking(new_g + new_h, new_g,
                                                    new_h, new_m, curr, t,
                                                    new_x, trustable,
                                                    self.order,
                                                    new_parikh_vec_lst,
                                                    curr.heuristic_priority)

                            start_time = timeit.default_timer()
                            self.open_set.heap_insert(tp)
                            self.queue += timeit.default_timer() - start_time
                            self.insertion += 1
                            self.max_rank = check_max_event(
                                tp, self.max_rank, t)
                        else:
                            tp = CacheReopenMarking(new_g + new_h, new_g,
                                                    new_h, new_m, curr, t, [],
                                                    trustable, self.order,
                                                    new_parikh_vec_lst,
                                                    curr.heuristic_priority)
                            self.infeasible_set[new_m] = tp
                # put all markings into open set, regardless of feasibleity of markings
                else:
                    if m_in_open_flag:
                        start_time = timeit.default_timer()
                        marking_to_explore = self.open_set.heap_get(new_m)
                        self.queue += timeit.default_timer() - start_time
                        # if shorter path found, update

                        if new_g < marking_to_explore.g:
                            marking_to_explore.p, marking_to_explore.t, marking_to_explore.g = curr, t, new_g
                            # update previous transition list and abandon path before
                            new_parikh_vec_lst = get_parikh_vec_lst(
                                curr.parikh_vec_lst, t_idx)
                            marking_to_explore.parikh_vec_lst = new_parikh_vec_lst
                            new_h, new_x, trustable = derive_multi_heuristic(
                                cost_vec, curr.x, t_idx, curr.h)
                            marking_to_explore.h = new_h
                            marking_to_explore.x = new_x
                            marking_to_explore.trust = trustable
                            marking_to_explore.f = marking_to_explore.g + new_h
                            start_time = timeit.default_timer()
                            self.open_set.heap_update(marking_to_explore)
                            self.queue += timeit.default_timer() - start_time
                            self.update += 1
                        # subsequent marking has equal path, but the heuristic change from infeasible to feasible
                        elif new_g == marking_to_explore.g:
                            temp_parikh_vec_lst = get_parikh_vec_lst(
                                curr.parikh_vec_lst, t_idx)
                            new_parikh_vec_lst, update_flag = update_parikh_vec_lst(
                                temp_parikh_vec_lst,
                                marking_to_explore.parikh_vec_lst)
                            # if new paths are found
                            if update_flag:
                                marking_to_explore.parikh_vec_lst = new_parikh_vec_lst
                                new_h, new_x, trust_flag = derive_multi_heuristic(
                                    cost_vec, curr.x, t_idx, curr.h)
                                # if the path is equally long, but the heuristic change from infeasible to feasible
                                if not marking_to_explore.trust and trust_flag:
                                    marking_to_explore.h = new_h
                                    marking_to_explore.f = marking_to_explore.h + marking_to_explore.g
                                    marking_to_explore.x = new_x
                                    marking_to_explore.trust_flag = True
                                    start_time = timeit.default_timer()
                                    self.open_set.heap_update(
                                        marking_to_explore)
                                    self.queue += timeit.default_timer(
                                    ) - start_time
                                    self.update += 1
                                elif marking_to_explore.trust and trust_flag:
                                    marking_to_explore.h = min(
                                        marking_to_explore.h, new_h)
                                    marking_to_explore.f = marking_to_explore.g + marking_to_explore.h
                                    marking_to_explore.x = concatenate_two_sol(
                                        marking_to_explore.x, new_x)
                                    start_time = timeit.default_timer()
                                    self.open_set.heap_update(
                                        marking_to_explore)
                                    self.queue += timeit.default_timer(
                                    ) - start_time
                                    self.update += 1
                    # the marking explored is not in open set
                    else:
                        self.order += 1
                        new_h, new_x, trustable = derive_multi_heuristic(
                            cost_vec, curr.x, t_idx, curr.h)
                        new_parikh_vec_lst = get_parikh_vec_lst(
                            curr.parikh_vec_lst, t_idx)
                        tp = CacheReopenMarking(new_g + new_h, new_g, new_h,
                                                new_m, curr, t, new_x,
                                                trustable, self.order,
                                                new_parikh_vec_lst,
                                                curr.heuristic_priority)
                        if trustable:
                            self.max_rank = check_max_event(
                                tp, self.max_rank, t)
                        start_time = timeit.default_timer()
                        self.open_set.heap_insert(tp)
                        self.queue += timeit.default_timer() - start_time
                        self.insertion += 1
示例#6
0
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
    lp_solved = 1

    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

        while not curr.trust:
            if (time.time() - start_time) > max_align_time_trace:
                return None

            already_closed = current_marking in closed
            if already_closed:
                curr = heapq.heappop(open_set)
                current_marking = curr.m
                continue

            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)
            lp_solved += 1

            # 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,
                                                     lp_solved=lp_solved)

        closed.add(current_marking)
        visited += 1

        enabled_trans = copy(trans_empty_preset)
        for p in current_marking:
            for t in p.ass_trans:
                if t.sub_marking <= current_marking:
                    enabled_trans.add(t)

        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)
示例#7
0
    def search(self):
        incidence_matrix = self.incidence_matrix
        ini_vec, fin_vec, cost_vec = self.__vectorize_initial_final_cost(
            self.incidence_matrix, self.ini, self.fin, self.cost_function)
        closed = set()
        cost_vec = [x * 1.0 for x in cost_vec]
        start_time = timeit.default_timer()
        h, x = compute_init_heuristic_without_split(
            np.array(np.array(fin_vec) - np.array(ini_vec)),
            np.array(incidence_matrix.a_matrix), np.array(cost_vec))
        self.heuristic_time += timeit.default_timer() - start_time
        ini_state = utils.SearchTuple(0 + h, 0, h, self.ini, None, None, x,
                                      True)
        open_set = [ini_state]
        self.num_insert += 1
        heapq.heapify(open_set)
        self.queue_time += timeit.default_timer() - start_time
        self.simple_lp = 1
        trans_empty_preset = set(t for t in incidence_matrix.transitions
                                 if len(t.in_arcs) == 0)
        while not len(open_set) == 0:
            start_time = timeit.default_timer()
            curr = heapq.heappop(open_set)
            self.queue_time += timeit.default_timer() - start_time
            self.num_removal += 1
            current_marking = curr.m
            while not curr.trust:
                already_closed = current_marking in closed
                if already_closed:
                    start_time = timeit.default_timer()
                    curr = heapq.heappop(open_set)
                    self.queue_time += timeit.default_timer() - start_time
                    self.num_removal += 1
                    current_marking = curr.m
                    continue
                start_time = timeit.default_timer()
                h, x = compute_init_heuristic_without_split(
                    np.array(fin_vec) -
                    np.array(incidence_matrix.encode_marking(curr.m)),
                    np.array(incidence_matrix.a_matrix), np.array(cost_vec))
                self.heuristic_time += timeit.default_timer() - start_time
                self.simple_lp += 1
                tp = utils.SearchTuple(curr.g + h, curr.g, h, curr.m, curr.p,
                                       curr.t, x, True)
                start_time = timeit.default_timer()
                curr = heapq.heappushpop(open_set, tp)
                self.queue_time += timeit.default_timer() - start_time
                self.num_insert += 1
                self.num_removal += 1
                current_marking = curr.m
            already_closed = current_marking in closed
            if already_closed:
                continue

            if curr.h < 0.01:
                if current_marking == self.fin:
                    return self._reconstruct_alignment(curr)

            closed.add(current_marking)
            self.visited += 1

            enabled_trans = copy(trans_empty_preset)
            for p in current_marking:
                for t in p.ass_trans:
                    if t.sub_marking <= current_marking:
                        enabled_trans.add(t)

            trans_to_visit_with_cost = [
                (t, self.cost_function[t]) for t in enabled_trans
                if not (t is not None and is_log_move(t, '>>')
                        and is_model_move(t, '>>'))
            ]

            for t, cost in trans_to_visit_with_cost:
                self.traversed += 1
                new_marking = utils.add_markings(current_marking,
                                                 t.add_marking)
                if new_marking in closed:
                    continue
                g = curr.g + cost
                h, x, trustable = derive_heuristic(
                    cost_vec, curr.x, incidence_matrix.transitions[t], curr.h)
                new_f = g + h
                tp = utils.SearchTuple(new_f, g, h, new_marking, curr, t, x,
                                       trustable)

                start_time = timeit.default_timer()
                heapq.heappush(open_set, tp)
                self.queue_time += timeit.default_timer() - start_time
                self.num_insert += 1
示例#8
0
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()
    heu_dict = {}
    heu_max_ind_dict = {}
    mtcgt_dict = {}

    parameters = {}
    parameters[marking_equation.Parameters.FULL_BOOTSTRAP_REQUIRED] = False
    parameters[marking_equation.Parameters.INCIDENCE_MATRIX] = incidence_matrix
    parameters[marking_equation.Parameters.COSTS] = cost_function

    visited = 0
    queued = 0
    traversed = 0
    me = marking_equation.build(sync_net, ini, fin, parameters=parameters)
    h, x = me.solve()
    lp_solved = 1

    # try to see if the firing sequence is already fine
    firing_sequence, reach_fm, explained_events = me.get_firing_sequence(x)
    if reach_fm:
        return __reconstruct_alignment(firing_sequence, h, visited, queued, traversed,
                                       ret_tuple_as_trans_desc=ret_tuple_as_trans_desc, lp_solved=lp_solved)
    mm, index = __get_model_marking_and_index(ini)
    __update_heu_dict(heu_dict, heu_max_ind_dict, mm, index, h, x, firing_sequence, incidence_matrix, cost_vec)

    ini_state = utils.TweakedSearchTuple(0 + h, 0, h, ini, None, None, x, True, False)
    open_set = [ini_state]
    heapq.heapify(open_set)

    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

        while not curr.trust:
            if (time.time() - start_time) > max_align_time_trace:
                return None

            already_closed = current_marking in closed
            if already_closed:
                curr = heapq.heappop(open_set)
                current_marking = curr.m
                continue

            if curr.t not in mtcgt_dict:
                lp_solved += 1
                mtcgt = __min_total_cost_given_trans(me, ini, incidence_matrix, curr.t)
                mtcgt_dict[curr.t] = mtcgt
            else:
                mtcgt = mtcgt_dict[curr.t]

            h1 = max(mtcgt - curr.g, 0)
            if h1 > curr.h:
                tp = utils.TweakedSearchTuple(curr.g + h1, curr.g, h1, curr.m, curr.p, curr.t, curr.x, False, False)
                curr = heapq.heappushpop(open_set, tp)
                current_marking = curr.m
                continue

            mm, index = __get_model_marking_and_index(curr.m)
            h2, x2, trust2 = __get_heu_from_dict(heu_dict, heu_max_ind_dict, mm, index)
            if h2 is not None and h2 > curr.h:
                tp = utils.TweakedSearchTuple(curr.g + h2, curr.g, h2, curr.m, curr.p, curr.t, x2, trust2, False)
                curr = heapq.heappushpop(open_set, tp)
                current_marking = curr.m
                continue

            me.change_ini_vec(curr.m)
            h, x = me.solve()

            __update_heu_dict_specific_point(heu_dict, heu_max_ind_dict, mm, index, h, x)

            lp_solved += 1
            tp = utils.TweakedSearchTuple(curr.g + h, curr.g, h, curr.m, curr.p, curr.t, x, True, True)
            curr = heapq.heappushpop(open_set, tp)
            current_marking = curr.m

        already_closed = current_marking in closed
        if already_closed:
            continue
        if curr.h < 0.01:
            if current_marking == fin:
                trans_list = __transitions_list_from_state(curr)
                return __reconstruct_alignment(trans_list, curr.f, visited, queued, traversed,
                                               ret_tuple_as_trans_desc=ret_tuple_as_trans_desc, lp_solved=lp_solved)

        if curr.virgin:
            # try to see if the firing sequence is already fine
            firing_sequence, reach_fm, explained_events = me.get_firing_sequence(curr.x)
            if reach_fm:
                trans_list = __transitions_list_from_state(curr) + list(firing_sequence)
                return __reconstruct_alignment(trans_list, curr.f, visited, queued, traversed,
                                               ret_tuple_as_trans_desc=ret_tuple_as_trans_desc, lp_solved=lp_solved)
            mm, index = __get_model_marking_and_index(curr.m)
            __update_heu_dict(heu_dict, heu_max_ind_dict, mm, index, h, x, firing_sequence, incidence_matrix, cost_vec)

        closed.add(current_marking)
        visited += 1

        enabled_trans = copy(trans_empty_preset)
        for p in current_marking:
            for t in p.ass_trans:
                if t.sub_marking <= current_marking:
                    enabled_trans.add(t)

        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)
            trust = utils.__trust_solution(x)
            mm, index = __get_model_marking_and_index(new_marking)

            if not trust:
                h2, x2, trust2 = __get_heu_from_dict(heu_dict, heu_max_ind_dict, mm, index)
                if h2 is not None and (h2 > h or trust2):
                    h = h2
                    x = x2
                    trust = trust2
            else:
                __update_heu_dict_specific_point(heu_dict, heu_max_ind_dict, mm, index, h, x)

            new_f = g + h
            tp = utils.TweakedSearchTuple(new_f, g, h, new_marking, curr, t, x, trust, False)
            heapq.heappush(open_set, tp)
示例#9
0
def __search_without_synchr(net,
                            ini,
                            fin,
                            log_trace,
                            skip=utils.SKIP,
                            ret_tuple_as_trans_desc=True,
                            max_align_time_trace=sys.maxsize,
                            expo=2):
    '''
    In this function that can be called with the following way:
            alignment.algorithm.apply(trace, net, marking, fmarking,variant=ali.VERSION_DIJKSTRA_EXPONENTIAL_HEURISTIC,
                                parameters={ali.Parameters.SYNCHRONOUS:False})
    we compute the distance at each marking. However there is a question:
    Should we keep the entire trace or cut it?
    Notice that the heuristic on marking reachability is ON
    '''
    trace = [log_trace[i]["concept:name"] for i in range(len(log_trace))]
    start_time = time.time()
    decorate_transitions_prepostset(net)
    decorate_places_preset_trans(net)
    closed = {}

    # a node in this version contains the marking state.m[Ø] and the position in the trace state.m[1]
    ini_state = utils.DijkstraSearchTuple(0, (ini, 0), None, None, 0)
    open_set = [ini_state]
    heapq.heapify(open_set)
    visited = 0
    queued = 0
    traversed = 0

    def cost_function(t, l, expo):
        if t == utils.SKIP:
            return expo**(-l)
        else:
            return 0

    trans_empty_preset = set(t for t in 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
        already_closed = current_marking in closed.keys()

        if already_closed:
            # if marking has already been visited, we don't visit it again
            # it's a heuristic
            continue

        if current_marking[0] == fin and current_marking[1] == len(trace):
            return utils.__reconstruct_alignment(
                curr,
                visited,
                queued,
                traversed,
                ret_tuple_as_trans_desc=ret_tuple_as_trans_desc)

        closed[current_marking] = curr.l

        visited += 1

        # ----- either we try to move in model
        possible_enabling_transitions = copy(trans_empty_preset)
        for p in current_marking[0]:
            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[0]
        ]

        trans_to_visit_with_cost = [(t, cost_function(utils.SKIP, curr.l,
                                                      expo))
                                    for t in enabled_trans if t is not None]
        for t, cost in trans_to_visit_with_cost:
            traversed += 1
            new_marking = (utils.add_markings(current_marking[0],
                                              t.add_marking),
                           current_marking[1])

            if new_marking in closed.keys():
                # if marking has already been visited, we don't visit it again
                # it's a heuristic
                continue

            queued += 1

            tp = utils.DijkstraSearchTuple(curr.g + cost, new_marking, curr,
                                           (">>", t), curr.l + 1)

            heapq.heappush(open_set, tp)

        # ------ either we try to move in log
        if current_marking[1] < len(trace):
            traversed += 1
            new_marking = (current_marking[0], current_marking[1] + 1)
            if new_marking in closed.keys():
                # if marking has already been visited, we don't visit it again
                # it's a heuristic
                continue
            queued += 1
            tp = utils.DijkstraSearchTuple(
                curr.g + cost_function(utils.SKIP, curr.l, expo), new_marking,
                curr, (trace[current_marking[1]], ">>"), curr.l + 1)

            heapq.heappush(open_set, tp)

            # ------ either we try to move in both (synchronous moves)
            trans_to_visit_with_cost = [
                (t, cost_function(t, curr.l, expo)) for t in enabled_trans
                if t is not None and str(t) == trace[current_marking[1]]
            ]
            for t, cost in trans_to_visit_with_cost:
                traversed += 1
                new_marking = (utils.add_markings(current_marking[0],
                                                  t.add_marking),
                               current_marking[1] + 1)

                if new_marking in closed.keys():
                    # if marking has already been visited, we don't visit it again
                    # it's a heuristic
                    continue

                queued += 1

                tp = utils.DijkstraSearchTuple(curr.g + cost, new_marking,
                                               curr,
                                               (trace[current_marking[1]], t),
                                               curr.l + 1)

                heapq.heappush(open_set, tp)
示例#10
0
def __search_with_synchr(sync_net,
                         ini,
                         fin,
                         skip,
                         ret_tuple_as_trans_desc=False,
                         max_align_time_trace=sys.maxsize,
                         expo=2):
    '''
    In this function that can be called with the following way:
            alignment.algorithm.apply(trace, net, marking, fmarking,variant=ali.VERSION_DIJKSTRA_EXPONENTIAL_HEURISTIC,
                                parameters={ali.Parameters.SYNCHRONOUS:True})
    Cost of transition depends on the run of the synchronous product.
    Other parameters:
    ali.Parameters.EXPONENT:2 (change the base of the log)
    '''
    start_time = time.time()
    decorate_transitions_prepostset(sync_net)
    decorate_places_preset_trans(sync_net)
    closed = {}

    ini_state = utils.DijkstraSearchTuple(0, ini, None, None, 0)
    open_set = [ini_state]
    heapq.heapify(open_set)
    visited = 0
    queued = 0
    traversed = 0

    def cost_function(t, l, expo):
        if t.label is None:
            return expo**(-l)
        if t.label[1] == utils.SKIP or t.label[0] == utils.SKIP:
            return expo**(-l)
        else:
            return 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
        already_closed = current_marking in closed.keys()

        if already_closed:
            continue

        if current_marking == fin:
            return utils.__reconstruct_alignment(
                curr,
                visited,
                queued,
                traversed,
                ret_tuple_as_trans_desc=ret_tuple_as_trans_desc)

        closed[current_marking] = curr.l
        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, curr.l, expo))
                                    for t in enabled_trans if t is not None]

        for t, cost in trans_to_visit_with_cost:
            traversed += 1
            new_marking = utils.add_markings(current_marking, t.add_marking)

            already_closed = new_marking in closed.keys()
            if already_closed:
                continue

            queued += 1

            tp = utils.DijkstraSearchTuple(curr.g + cost, new_marking, curr, t,
                                           curr.l + 1)

            heapq.heappush(open_set, tp)