Exemple #1
0
def psolC_generalized(g, W0, W1):
    # base case : game is empty
    if g.get_nodes() == []:
        if DEBUG_PRINT: print("Base case return")
        return g, W0, W1

    # else retrieve useful information on the game
    nbr_func = g.get_nbr_priority_functions()  # number of functions
    priorities = [[] for z in xrange(nbr_func)]  # setup list containing list of priorities for each function
    even_priorities = [[] for z in xrange(nbr_func)]  # setup list containing list of even priorities for each function

    # first, retrieve all priorities and put them in the lists of priorities for each function
    for node in g.nodes.iterkeys():
        for func in range(nbr_func):
            priorities[func].append(g.get_node_priority_function_i(node, func + 1))  # function are numbered 1 to k

    # sort priorities and create the lists containing only the even priorities
    for func in range(nbr_func):
        priorities[func] = sorted(set(priorities[func]), reverse=False)  # change into set to remove duplicates and sort
        even_priorities[func] = filter(lambda x: x % 2 == 0, priorities[func])  # keep the sorted even priorities

        # if there are no even priorities according to one of the functions, the game is completely won by player 1
        # return empty game and all nodes added to W1
        if len(even_priorities[func]) == 0:
            W1.extend(g.nodes.keys())
            return Graph(), W0, W1

    if DEBUG_PRINT:
        print("Priorities " + str(priorities))
        print("Even priorities " + str(even_priorities))


    # handle odd priorities by calling psolC with the correct function
    for i in range(1, nbr_func + 1):
        safe_episodes = jfs_algo_func(g, i, 1)
        if len(safe_episodes) > 0:
            A, complement = attractor(g, safe_episodes, 1)
            W1.extend(A)
            subgame = g.subgame(complement)
            return psolC_generalized(subgame, W0, W1)

    # handle even priorities
    w = truc(g, nbr_func, even_priorities, priorities)
    if len(w) > 0:
        A, complement = attractor(g, w, 0)
        W0.extend(A)
        subgame = g.subgame(complement)
        return psolC_generalized(subgame, W0, W1)

    return g, W0, W1
Exemple #2
0
def psolC(g, W1, W2):
    safe_episodes = jfs_algo(g, 0)
    subgame = g
    if len(safe_episodes) > 0:
        A, complement = attractor(subgame, safe_episodes, 0)
        W1.extend(A)
        subgame = subgame.subgame(complement)
        subgame, W1, W2 = psolC(subgame, W1, W2)
    safe_episodes = jfs_algo(subgame, 1)
    if len(safe_episodes) > 0:
        A, complement = attractor(subgame, safe_episodes, 1)
        W2.extend(A)
        subgame = subgame.subgame(complement)
        subgame, W1, W2 = psolC(subgame, W1, W2)
    return subgame, W1, W2
Exemple #3
0
def psolB_modified_att(g, W1, W2):
    """
    Partial solver psolB for parity games using fatal attractors. Implementation using a variant of attractors.
    :param g: a game graph.
    :return: a partial solution g', W1', W2' in which g is an unsolved subgame of g and W1', W2' are included in the
    two respective winning regions of g.
    """

    for color in sort_colors_ascending(g):

        if DEBUG_PRINT: print("Computing for color " + str(color))

        target_set = ops.i_priority_node(g, color)  # set of nodes of color 'color'

        cache = set()

        # TODO check list comparison efficiency
        while cache != set(target_set) and target_set != []:

            cache = target_set

            MA, in_att, rest = monotone_attractor_including_target(g, target_set, color)

            if DEBUG_PRINT: print(" MA " + str(MA) + " Player " + str(g.get_node_player(target_set[0])) + "\n")

            # if the sum of values is the length of the target set, every node in target is in the attractor
            if sum(in_att.itervalues()) == len(target_set):

                if DEBUG_PRINT: print("Set " + str(target_set) + " in MA ")

                att, complement = attractor(g, MA, color % 2)

                if color % 2 == 0:
                    W1.extend(att)
                else:
                    W2.extend(att)

                return psolB_modified_att(g.subgame(complement), W1, W2)

            else:
                # only keep in target the target nodes that are included in the attractor
                target_set = filter(lambda x: in_att[x] == 1, in_att.iterkeys())

    return g, W1, W2
Exemple #4
0
def psolB_set(g, W1, W2):
    """
    Partial solver psolB for parity games using fatal attractors. Implementation using sets.
    :param g: a game graph.
    :return: a partial solution g', W1', W2' in which g is an unsolved subgame of g and W1', W2' are included in the
    two respective winning regions of g.
    """

    empty_set = set()

    for color in sort_colors_ascending(g):

        if DEBUG_PRINT: print("Computing for color " + str(color))

        target_set = set(ops.i_priority_node(g, color))  # set of nodes of color 'color'

        cache = set()

        while cache != target_set and target_set != empty_set:

            cache = target_set

            MA, rest = monotone_attractor(g, target_set, color)

            if DEBUG_PRINT: print(" MA " + str(MA) + " Player " + str(g.get_node_player(target_set[0])) + "\n")

            if target_set.issubset(MA):

                if DEBUG_PRINT: print("Set " + str(target_set) + " in MA ")

                att, complement = attractor(g, MA, color % 2)

                if color % 2 == 0:
                    W1.extend(att)
                else:
                    W2.extend(att)

                return psolB_set(g.subgame(complement), W1, W2)

            else:
                target_set = target_set.intersection(MA)

    return g, W1, W2
Exemple #5
0
def psolB_buchi_safety(g, W1, W2):
    """
    Partial solver psolB for parity games. This implementation uses buchi inter safety to find fatal attractors.
    :param g: a game graph.
    :return: a partial solution g', W1', W2' in which g is an unsolved subgame of g and W1', W2' are included in the
    two respective winning regions of g.
    """

    for color in sort_colors_ascending(g):

        if DEBUG_PRINT: print("Computing for color " + str(color))

        target_set = ops.i_priority_node(g, color)  # set of nodes of color 'color'

        # TODO here replace previous call by a call which goes through each node and add it to in/out set of color i
        # we only go trough every node once and add it to greater than color or of priority color

        # record the nodes we don't want to see infinitely often i.e. the ones with greater priority than color
        not_target_set_bigger = []
        for node in g.nodes.iterkeys():
            if g.get_node_priority(node) > color:
                not_target_set_bigger.append(node)

        # winning region of buchi inter safety for player color % 2
        w = safety.buchi_inter_safety_player(g, target_set, not_target_set_bigger, color % 2)

        if DEBUG_PRINT: print(" MA " + str(w) + " Player " + str(g.get_node_player(target_set[0])) + "\n")

        if w != []:

            if DEBUG_PRINT: print("Set " + str(target_set) + " in MA ")

            att, complement = attractor(g, w, color % 2)

            if color % 2 == 0:
                W1.extend(att)
            else:
                W2.extend(att)

            return psolB_buchi_safety(g.subgame(complement), W1, W2)

    return g, W1, W2
def psolB_generalized_inline(g, W1, W2):
    """
    Adaptation of partial solver psolB for generalized parity games. This implementation uses inline version of
    generalized buchi inter safety games.
    :param g: a game graph.
    :return: a partial solution g', W1', W2' in which g is an unsolved subgame of g and W1', W2' are included in the
    two respective winning regions of g.
    """

    # base case : game is empty
    if g.get_nodes() == []:
        return g, W1, W2

    # else retrieve useful information on the game
    nbr_func = g.get_nbr_priority_functions()  # number of functions
    priorities = [[] for z in xrange(nbr_func)]  # setup list containing list of priorities for each function
    even_priorities = [[] for z in xrange(nbr_func)]  # setup list containing list of even priorities for each function
    sizes = [0] * nbr_func  # setup the sizes for the lists of priorities
    even_sizes = [0] * nbr_func  # setup the sizes for the lists of even priorities
    empty_set = set()  # useful when computing fatal attractor for player 1

    # first, retrieve all priorities and put them in the lists of priorities for each function
    for node in g.nodes.iterkeys():
        for func in range(nbr_func):
            priorities[func].append(g.get_node_priority_function_i(node, func + 1))  # function are numbered 1 to k

    # sort priorities and create the lists containing only the even priorities
    for func in range(nbr_func):
        # TODO we transform into set to remove duplicate, might check itertools, ordered dicts and heaps also
        priorities[func] = sorted(set(priorities[func]), reverse=True)  # change into set to remove duplicates and sort
        even_priorities[func] = filter(lambda x: x % 2 == 0, priorities[func])

        # if there are no even priorities according to one of the functions, the game is completely won by player 1
        # return empty game and all nodes added to W2
        if len(even_priorities[func]) == 0:
            W2.extend(g.nodes.keys())
            return Graph(), W1, W2

        sizes[func] = len(priorities[func])
        even_sizes[func] = len(even_priorities[func])

    # here we have sorted lists of priorities as well as their sizes

    indexes = [0] * nbr_func  # index for each function to go trough its priorities
    depth = 0  # depth is needed for the level of the lattice
    max_size = max(even_sizes)  # needed for the maximum level of the lattice

    if DEBUG_PRINT:
        print("priorities " + str(priorities))
        print("even priorities " + str(even_priorities))
        print("sizes " + str(sizes))
        print("depth " + str(depth))
        print("max_size " + str(max_size))

    # TODO instead of doing as above, go through nodes in iterator order and for each node add priorities to the list
    # of priorities and even priorities. Then we work on those unsorted lists. The order is random but it might still
    # work while avoiding to sort the lists.

    # while we have not considered every couple of the lattice i.e. not reached the maximal depth for the levels in
    # the lattice for the even priorities or we have not considered every priority in the list of priorities
    while (not all(indexes[w] == sizes[w] for w in range(nbr_func))) or (depth != max_size + 2):

        if DEBUG_PRINT: print("iteration " + str(depth) + " indexes " + str(indexes))

        # for each function, we treat odd priorities in order in the list until we have reached an even priority
        for i in range(nbr_func):

            if DEBUG_PRINT: print("     function " + str(i))

            # while we can advance in the list and we encounter an odd priority, we consider it
            while indexes[i] < sizes[i] and priorities[i][indexes[i]] % 2 == 1:

                if DEBUG_PRINT:
                    print("           index " + str(indexes[i]) + " element " + str(priorities[i][indexes[i]]))
                    print("           fonction " + str(i + 1) + " " + str(priorities[i][indexes[i]]))

                # we have an odd priority to consider
                odd_priority = priorities[i][indexes[i]]

                # set of nodes of color 'odd_priority' according to function i+1
                target_set = set(ops.i_priority_node_function_j(g, odd_priority, i + 1))

                # perform fixpoint computation to find fatal attractor for player odd

                cache = set()

                while cache != target_set and target_set != empty_set:

                    cache = target_set

                    MA, rest = monotone_attractor(g, target_set, odd_priority, i + 1)

                    if DEBUG_PRINT: print(" MA " + str(MA) + " Player " + str(1) + "\n")

                    if target_set.issubset(MA):

                        if DEBUG_PRINT: print("Set " + str(target_set) + " in MA ")

                        att, complement = attractor(g, MA, 1)

                        W2.extend(att)

                        return psolB_generalized_inline(g.subgame(complement), W1, W2)

                    else:
                        target_set = target_set.intersection(MA)

                # if we have not found a fatal attractor, we go forward in the list and restart the same logic until
                # reaching an even priority or the end of the list
                indexes[i] += 1

            # we have found an even priority at position indexes[i], at next iteration of the outer while, we restart
            # from the next index in the list
            if indexes[i] < sizes[i]:
                indexes[i] += 1

        # when this is reached, we know we have handled every odd priorities until reaching an even priority for each
        # function i.e. if [5,3,4,1,0] and [2,1,0] are the priorities, after first iteration of outer while we have
        # handled 5, 3 and reached 4 in the first list and reached 2 in the second (assuming there was no recursive
        # call after handling 5 and 3).

        # TODO if we have gone trough the whole list for each function, player 2 wins ? i.e. index is list size for each
        # TODO bug : sometimes there are no even priorities according to some function : even_tuples_iterator bugs
        # go through every k-uple of even priorities in the current level
        for kuple in even_tuples_iterator(depth, even_priorities, even_sizes, [0] * nbr_func, nbr_func, 0):

            if DEBUG_PRINT: print("     " + str(kuple))

            # we now will compute a generalized buchi inter safety game
            avoid = []  # nodes for the safety game, to be avoided
            sets_gen = [[] for z in xrange(nbr_func)]  # sets of nodes to be visited infinitely often

            for nod in g.nodes.iterkeys():

                flag = False  # is the node to be avoided (greater priority than required for some function)

                # for each function
                for f in range(1, nbr_func + 1):

                    # priority of the node according to that function
                    prio = g.get_node_priority_function_i(nod, f)

                    if DEBUG_PRINT:
                        print("          " + str(prio))
                        print("          " + str(prio % 2 == 1) + " " + str(prio > kuple[f - 1]))

                    # priority is odd and greater than the corresponding one in the k-uple, we want to avoid the node
                    if prio % 2 == 1 and prio > kuple[f - 1]:
                        flag = True
                    # else if the priority is the one we want to see, add it to the set to be visited infinitely often
                    elif prio == kuple[f - 1]:
                        sets_gen[f - 1].append(nod)

                # if the flag is true, we don't want to see this node
                if flag:
                    avoid.append(nod)

            if DEBUG_PRINT:
                print("sets " + str(sets_gen))
                print("avoid " + str(avoid))

            # solution of the generalized buchi inter safety game
            win = generalized_buchi_inter_safety.inlined_generalized_buchi_inter_safety(g, sets_gen, avoid)

            if DEBUG_PRINT:
                print(g)
                print("=========================== win " + str(win))

            # if we have some winning region
            if len(win) != 0:
                att2, comp = attractor(g, win, 0)
                W1.extend(att2)
                return psolB_generalized_inline(g.subgame(comp), W1, W2)

        depth += 1

    return g, W1, W2
Exemple #7
0
def psolQ_classical_attractor(g, W1, W2):
    """
    Partial solver psolQ for parity games using fatal attractors. This implementation uses classical attractors, where
    the nodes in the target set are in the attractor by default.
    :param g: a game graph.
    :return: a partial solution g', W1', W2' in which g is an unsolved subgame of g and W1', W2' are included in the
    two respective winning regions of g.
    """

    # base case : game is empty
    if g.get_nodes() == []:
        return g, W1, W2

    # else sort priorities and retrieve maximum even and odd priorities
    colors_descending = sort_colors_descending(g)
    empty_set = set()  # useful later

    if DEBUG_PRINT: print("Colors descending" + str(colors_descending))

    max_prio = colors_descending[0]
    max_prio_player = max_prio % 2

    if max_prio_player == 0:
        max_even = max_prio
        max_odd = max_prio - 1
    if max_prio_player == 1:
        max_even = max_prio - 1
        max_odd = max_prio

    if DEBUG_PRINT:
        print("MAX EVEN " + str(max_even))
        print("MAX ODD " + str(max_odd))

    for color in colors_descending:

        if DEBUG_PRINT: print("Computing for color " + str(color))

        color_player = color % 2

        # set containing nodes of g with priority >= d and parity color_player
        x = set(
            filter(
                lambda z: g.get_node_priority(z) % 2 == color_player and g.
                get_node_priority(z) >= color, g.nodes.iterkeys()))

        if DEBUG_PRINT:
            print("NODES COLOR BIGGER THAN " + str(color) + " " + str(x))

        cache = set()

        while cache != x and x != empty_set:

            if DEBUG_PRINT: print("SET X" + str(x))

            cache = x

            if color_player == 0:
                MA, rest = layered_classical_attractor(g, max_even, x)
            else:
                MA, rest = layered_classical_attractor(g, max_odd, x)

            if DEBUG_PRINT:
                print(" MA " + str(MA) + " Player " +
                      str(g.get_node_player(x[0])) + "\n")

            # Here instead of check for inclusion of the target set x in the attractor MA we consider the nodes in x and
            # check wether they can force for the correct player to go back in the attractor. We can't simply check for
            # inclusion of x in MA since in this version the target set x is added de facto in the attractor
            # we also can't put this step directly inside the attractor, by checking for inclusion of the target set in
            # the attractor because here we check the inclusion in the layered attractor and not in the attractor at a
            # specific step

            is_ok = True

            # create a new version of x which only contains the node in x from which player color_player can ensure to
            # reach MA
            x_new = set()

            # for each node in x
            for node in x:

                # if node belongs to color player, it needs to have one successor in MA
                if g.get_node_player(node) == color_player:
                    for succ in g.get_successors(node):
                        if succ in MA:
                            x_new.add(node)
                            break
                else:
                    # if node belongs to other player, all its successors must be in MA
                    found = True  # does the node have all of its successors in MA
                    for succ in g.get_successors(node):
                        if not (succ in MA):
                            # we have found a successor not in MA
                            found = False
                            break
                    if found:
                        x_new.add(node)

            # if the length of x is different than the length of x_new, we have removed nodes from x and need to
            # continue. Else, the attractor is fatal
            if len(x) != len(x_new):
                is_ok = False

            # replace x by x_new, no intersection with MA can be done per above reasons, but intersection with MA
            # is done implicitly i.e. x_new is actually the intersection
            x = x_new

            if is_ok:

                # attractor is fatal

                if DEBUG_PRINT: print("Set " + str(x) + " in MA ")

                att, complement = attractor(g, MA, color_player)

                if color_player == 0:
                    W1.extend(att)
                else:
                    W2.extend(att)

                if DEBUG_PRINT:
                    print("TEMP W1 " + str(W1))
                    print("TEMP W2 " + str(W2))
                    print("TEMP REST " + str(complement))

                return psolQ_classical_attractor(g.subgame(complement), W1, W2)

    return g, W1, W2
Exemple #8
0
def psolQ(g, W1, W2):
    """
    Partial solver psolQ for parity games using fatal attractors.
    :param g: a game graph.
    :return: a partial solution g', W1', W2' in which g is an unsolved subgame of g and W1', W2' are included in the
    two respective winning regions of g.
    """

    # base case : game is empty
    if g.get_nodes() == []:
        return g, W1, W2

    # else sort priorities and retrieve maximum even and odd priorities
    colors_descending = sort_colors_descending(g)
    empty_set = set()  # useful later

    if DEBUG_PRINT: print("Colors descending" + str(colors_descending))

    max_prio = colors_descending[0]
    max_prio_player = max_prio % 2

    if max_prio_player == 0:
        max_even = max_prio
        max_odd = max_prio - 1
    if max_prio_player == 1:
        max_even = max_prio - 1
        max_odd = max_prio

    if DEBUG_PRINT:
        print("MAX EVEN " + str(max_even))
        print("MAX ODD " + str(max_odd))

    for color in colors_descending:

        if DEBUG_PRINT: print("Computing for color " + str(color))

        color_player = color % 2

        # set containing nodes of g with priority >= d and parity color_player
        x = set(
            filter(
                lambda z: g.get_node_priority(z) % 2 == color_player and g.
                get_node_priority(z) >= color, g.nodes.iterkeys()))

        if DEBUG_PRINT:
            print("NODES COLOR BIGGER THAN " + str(color) + " " + str(x))

        cache = set()

        while cache != x and x != empty_set:

            if DEBUG_PRINT: print("SET X" + str(x))

            cache = x

            if color_player == 0:
                MA, rest = layered_attractor(g, max_even, x)
            else:
                MA, rest = layered_attractor(g, max_odd, x)

            if DEBUG_PRINT:
                print(" MA " + str(MA) + " Player " +
                      str(g.get_node_player(x[0])) + "\n")

            if x.issubset(MA):

                if DEBUG_PRINT: print("Set " + str(x) + " in MA ")

                att, complement = attractor(g, MA, color_player)

                if color % 2 == 0:
                    W1.extend(att)
                else:
                    W2.extend(att)

                if DEBUG_PRINT:
                    print("TEMP W1 " + str(W1))
                    print("TEMP W2 " + str(W2))
                    print("TEMP REST " + str(complement))

                return psolQ(g.subgame(complement), W1, W2)

            else:
                x = x.intersection(MA)

    return g, W1, W2