Пример #1
0
def __calculate_heuristics(prev_h, prev_x, m0, index, corresp, t0, sync_net, incidence_matrix,
                           fin_vec, cost_vec, a_matrix, g_matrix, h_cvx, variant, use_cvxopt=False,
                           compute_exact_heu=False):
    """
    Calculate the heuristics

    Returns
    ---------------
    h
        Heuristic value
    x
        Solution
    """
    m, t = get_corresp_marking_and_trans(m0, index, corresp, t0)
    h = 0
    x = None

    if compute_exact_heu or t is None:
        h, x = utils.__compute_exact_heuristic_new_version(sync_net, a_matrix, h_cvx, g_matrix, cost_vec,
                                                           incidence_matrix, m, fin_vec, variant, use_cvxopt=use_cvxopt)
        trustable = True
    else:
        h, x = utils.__derive_heuristic(incidence_matrix, cost_vec, prev_x, t, prev_h)
        trustable = utils.__trust_solution(x)

    return h, x, trustable
def __update_heu_dict(heu_dict, heu_max_ind_dict, mm, index, h, x,
                      firing_sequence, incidence_matrix, cost_vec):
    """
    Updates the heuristics dictionary on the new marking, storing the information about the heuristics
    and the vector
    """
    x = copy(x)
    __update_heu_dict_specific_point(heu_dict, heu_max_ind_dict, mm, index, h,
                                     x)
    firing_sequence = list(firing_sequence)
    while firing_sequence:
        t = firing_sequence.pop(0)
        h, x = utils.__derive_heuristic(incidence_matrix, cost_vec, x, t, h)
        mm = semantics.weak_execute(t, mm)
        __update_heu_dict_specific_point(heu_dict, heu_max_ind_dict, mm, index,
                                         h, x)
def __search(sync_net, ini, fin, stop, cost_function, skip, max_trace_length):
    from pm4py.objects.petri.utils import decorate_places_preset_trans, decorate_transitions_prepostset

    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()

    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_factory.DEFAULT_LP_SOLVER_VARIANT == lp_solver_factory.CVXOPT_SOLVER_CUSTOM_ALIGN or lp_solver_factory.DEFAULT_LP_SOLVER_VARIANT == lp_solver_factory.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_factory.DEFAULT_LP_SOLVER_VARIANT,
        use_cvxopt=use_cvxopt)
    # heuristics need to be adapted for prefix alignments
    # here we make the heuristics way less powerful
    h = h / (max_trace_length + 1.0)
    ini_state = SearchTuple(0 + h, 0, h, ini, None, None, x, True)
    open_set = [ini_state]
    heapq.heapify(open_set)
    visited = 0
    queued = 0
    traversed = 0
    while not len(open_set) == 0:
        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_factory.DEFAULT_LP_SOLVER_VARIANT,
                use_cvxopt=use_cvxopt)
            # heuristics need to be adapted for prefix alignments
            # here we make the heuristics way less powerful
            h = h / (max_trace_length + 1.0)

            # 11/10/19: shall not a state for which we compute the exact heuristics be
            # by nature a trusted solution?
            tp = 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_factory.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
        enab_trans = [
            x for x in petri.semantics.enabled_transitions(
                sync_net, current_marking)
        ]
        if stop <= current_marking:
            #print(utils.__reconstruct_alignment(curr, visited, queued, traversed))
            enab_trans = [
                x for x in sync_net.transitions
                if x.sub_marking <= current_marking
            ]
            return current_marking
        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 (curr.t is not None and utils.__is_log_move(curr.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)
            # heuristics need to be adapted for prefix alignments
            # here we make the heuristics way less powerful
            h = h / (max_trace_length + 1.0)

            trustable = utils.__trust_solution(x)
            new_f = g + h

            tp = SearchTuple(new_f, g, h, new_marking, curr, t, x, trustable)
            heapq.heappush(open_set, tp)
Пример #4
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

        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,
             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

        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)
            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)