Пример #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 __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)
Пример #3
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)
Пример #4
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)