Beispiel #1
0
def attractor(g, U, j):
    """
    Computes the attractor for player j of the set U in g. Does not create any strategy and only returns the set that
    corresponds to the attractor.
    :param g: the game graph.
    :param U: the target set.
    :param j: the player for which we compute the attractor.
    :return: W the set of nodes corresponding to the attractor.
    """
    out = init_out(g)  # init out
    queue = deque(
    )  # init queue (deque is part of standard library and allows O(1) append() and pop() at either end)
    # this dictionary is used to know if a node belongs to a winning region without
    # iterating over both winning regions lists (we can check in O(1) in average)
    regions = defaultdict(lambda: -1)
    W = []  # the attractor
    opponent = op.opponent(j)  # player j's opponent

    # for each node in the target set U
    for node in U:
        queue.append(node)  # add node to the end of the queue
        regions[
            node] = j  # set its regions to j (node is winning for j because reachability objective is satisfied)
        W.append(node)  # add the node to the winning region list of j

    # while queue is not empty
    while queue:

        s = queue.popleft(
        )  # remove and return node on the left side of the queue (first in, first out)

        # iterating over the predecessors of node s
        for sbis in g.get_predecessors(s):

            if regions[
                    sbis] == -1:  # if sbis is not yet visited, its region is -1 by default
                if g.get_node_player(sbis) == j:

                    # belongs to j, set regions and strategy accordingly
                    queue.append(sbis)
                    regions[sbis] = j
                    W.append(sbis)

                elif g.get_node_player(sbis) == opponent:
                    # belongs to j bar, decrement out. If out is 0, set the region accordingly
                    out[sbis] -= 1

                    if out[sbis] == 0:
                        queue.append(sbis)
                        regions[sbis] = j
                        W.append(sbis)

    Wbis = []
    for node in g.get_nodes():
        if regions[node] != j:
            Wbis.append(node)

    return W, Wbis
Beispiel #2
0
def R_set(g, target_set, j):
    """
    We compute the attractor of a set of node-priority pairs where the priority
    represents the maximal priority seen so far.
    """
    ascending_priorities = g.get_sorted_priorities()
    v_out = init_out(g)  # a counter for visited edges from each vertex
    out = {(v, p): v_out[v]
           for v in g.get_nodes() for p in ascending_priorities}
    regions = defaultdict(lambda: -1)
    adversary = operations.opponent(j)

    # we keep a queue of newly found winning vertex-priority pairs
    queue = deque(target_set)
    while queue:
        (node, priority) = queue.popleft()
        for pred in g.get_predecessors(node):
            pred_player = g.get_node_player(pred)
            pred_priority = g.get_node_priority(pred)
            if pred_priority > priority:
                continue  # cannot be a predecessor
            if priority > g.get_node_priority(node):
                options = [priority]
            else:
                assert (priority == g.get_node_priority(node))
                options = filter(
                    lambda x: x >= pred_priority and x <= priority,
                    ascending_priorities)
                assert (len(options) > 0)
            for p in options:
                if regions[(pred, p)] == -1:  # vertex-priority is undecided
                    if pred_player == j:
                        regions[(pred, p)] = j
                        if (pred, p) not in target_set:
                            queue.append((pred, p))
                    elif pred_player == adversary:
                        out[(pred, p)] -= 1
                        if out[(pred, p)] == 0:
                            regions[(pred, p)] = j
                            if (pred, p) not in target_set:
                                queue.append((pred, p))
    # prepare output
    W = set()
    for n in g.get_nodes():
        if regions[(n, g.get_node_priority(n))] == j:
            W.add(n)
    return W
Beispiel #3
0
def safe_attractor(g, U, Ubis, j):
    out = init_out(g)
    queue = deque()
    regions = defaultdict(lambda: -1)
    opponent = op.opponent(j)

    for node in set(U) - set(Ubis):
        queue.append(node)  # add node to the end of the queue
        regions[
            node] = j  # set its regions to j (node is winning for j b/c reachability is satisfied)

    while queue:
        s = queue.popleft(
        )  # remove and return node on the left side of the queue (first in, first out)
        # iterating over the predecessors of node s
        for sbis in set(g.get_predecessors(s)) - set(Ubis):
            if regions[
                    sbis] == -1:  # if sbis is not yet visited, its region is -1 by default
                if g.get_node_player(sbis) == j:
                    # belongs to j, set regions and strategy accordingly
                    queue.append(sbis)
                    regions[sbis] = j
                elif g.get_node_player(sbis) == opponent:
                    # belongs to j bar, decrement out. If out is 0, set the region accordingly
                    out[sbis] -= 1
                    if out[sbis] == 0:
                        queue.append(sbis)
                        regions[sbis] = j

    W = []
    Wbis = []
    for node in g.get_nodes():
        if regions[node] == j:
            W.append(node)
        else:
            Wbis.append(node)

    return W, Wbis
def permissive_monotone_attractor(g, v_star, target_set, current_priorities):
    """
    Attractor for one layer, handling previously computed v_star
    :param g: a game graph.
    :param v_star: the previously computed v_star.
    :param target_set: the target set (nodes with memory).
    :param current_priorities: the current priorities for this layer.
    :return:
    """

    out_base = init_out(g)  # init out in base game
    nbr_func = g.get_nbr_priority_functions()
    out = defaultdict(lambda: -1)  # this out is used for nodes with memory
    queue = deque(
    )  # init queue (deque is part of standard library and allows O(1) append() and pop() at either end)
    # this dictionary is used to know if a node belongs to a winning region without
    # iterating over both winning regions lists (we can check in O(1) in average)
    regions = defaultdict(lambda: -1)
    W = []  # the attractor
    j = 0  # the player for which we compute the attractor
    opponent = ops.opponent(j)  # player j's opponent

    # in the attractor we won't consider (v, M) such that v is in star, either in predecessors or in target
    star_attractor, not_star_attractor = attractor_color_vector(
        g, v_star, 0, current_priorities)

    # for each predecessor of nodes in star of the original game, decrement their counter as they have a successor
    # already winning
    for star in star_attractor:
        for pr in g.get_predecessors(star):
            out_base[pr] -= 1

    for node in target_set:
        # todo added 3 june morning because since this was not removed, the out[base] was -1 for node v in star att but
        # then some (v, M) was seen and a further decrement was done
        # only consider targets which are not in star
        if not node[0] in star_attractor:
            queue.append(node)  # add node to the end of the queue

    if DEBUG_PRINT:
        print("--- Monotone attractor ---")
        print(g)
        print("star attractor " + str(star_attractor))
        print("out base " + str(out_base))

        print("Set " + str(target_set) + " Player " + str(j) + " Opponent " +
              str(opponent) + " Prio " + str(current_priorities))
        print("Marked before start " + str(regions) + " Queue before start " +
              str(queue))

    # while queue is not empty
    while queue:

        if DEBUG_PRINT: print("     Queue " + str(queue))

        s = queue.popleft(
        )  # remove and return node on the left side of the queue (first in, first out)

        if DEBUG_PRINT: print("     Considering node " + str(s))

        # here we need to create the predecessors
        preds = create_predecessors(g, s, current_priorities, star_attractor)

        # iterating over the predecessors of node s
        for sbis in preds:

            sbis_node = sbis[0]
            sbis_memory = sbis[1]

            # get sbis info sbis = node, memory
            sbis_player = g.get_node_player(sbis_node)
            priority_ok = all(
                g.nodes[sbis_node][z + 1] %
                2 == 0 or g.nodes[sbis_node][z + 1] <= current_priorities[z]
                for z in range(nbr_func))

            # TODO verifying that the priorities are correct is done when predecessors are created
            if DEBUG_PRINT:
                print("         Considering predecessor " + str(sbis) +
                      " Is marked ? " + str(regions[sbis]) + "Player " +
                      str(sbis_player) + " Priority " +
                      str(current_priorities))

            if regions[
                    sbis] == -1:  # if sbis is not yet visited, its region is -1 by default

                # if node is the correct player and its priority is lower or equal, add it. If it is in the target set,
                # also consider it even if it breaks the condition on priority.
                # and (sbis_priority <= priority or sbis in target_set)
                # condition on priorities is true
                if sbis_player == j:

                    if DEBUG_PRINT:
                        print("             Predecessor " + str(sbis) +
                              " Added ")

                    # if node has not been considered yet (not already been in the queue) add it
                    # this is to avoid considering the same node twice, which can happen only for the target node and
                    # can mess up the decrementation of the counters for nodes of the opponent
                    if sbis not in target_set:
                        queue.append(sbis)

                    # mark accordingly and add to winning region
                    regions[sbis] = j
                    W.append(sbis)

                # if node is the opposite player and its priority is lower or equal, check its counter of successors
                # if it is in the target set, also consider it even if it breaks the condition on priority.
                # and (sbis_priority <= priority or sbis in target_set)
                elif sbis_player == opponent:

                    if out[sbis] == -1:
                        # first ime, init out
                        out[sbis] = out_base[sbis_node]
                        if DEBUG_PRINT:
                            print("init out node " + str(sbis) + " out " +
                                  str(out[sbis]) + " out base " +
                                  str(out_base[sbis_node]))

                    # belongs to j bar, decrement out. If out is 0, set the region accordingly
                    out[sbis] -= 1

                    if DEBUG_PRINT:
                        print("             Predecessor " + str(sbis) +
                              " Decrement, new count = " + str(out[sbis]))

                    if out[sbis] == 0:

                        if DEBUG_PRINT:
                            print("             Predecessor " + str(sbis) +
                                  " Added ")

                        # if node has not been considered yet (not already been in the queue) add it
                        if sbis not in target_set:
                            queue.append(sbis)

                        # mark accordingly and add to winning region
                        regions[sbis] = j
                        W.append(sbis)

    # every node that is not marked is not in the attractor, we filter them for speed
    Wbis = filter(lambda x: regions[x] != j, g.nodes.iterkeys())

    if DEBUG_PRINT:
        print("Attractor " + str(W) + " Complement " + str(Wbis))
        print("-------------------------\n")

    return W, Wbis
Beispiel #5
0
def monotone_attractor_including_target(g, target_set, color):
    """
    Computes the monotone attractor of the target set, meaning the attractor without visiting bigger priorities than
    the one of the target set. This implementation adds the target set in the attractor at the start and checks during
    computation if the nodes of that target set would have been added to the attractor had they not been added at the
    start. This differs from the usual implementation which does not add the target set to the attractor at the start.
    In theory, this should allow us a quicker check of inclusion of the target set in the attractor.
    :param g: a game graph.
    :param target_set: a target set of nodes.
    :param color: the color of the nodes in target set.
    :return: W, in_att, Wbis the attractor, a dictionary of which nodes in the target set are in the attractor and the
    nodes which are not in the attractor.
    """

    p = color  # priority of the node gives us the player for which we compute the attractor
    out = init_out(g)  # init out
    queue = deque()  # init queue (deque is part of standard library and allows O(1) append() and pop() at either end)
    # this dictionary is used to know if a node belongs to a winning region without
    # iterating over both winning regions lists (we can check in O(1) in average)
    regions = defaultdict(lambda: -1)
    W = []  # the attractor
    j = p % 2  # the player for which we compute the attractor
    opponent = ops.opponent(j)  # player j's opponent

    in_att = defaultdict(lambda: 0)

    # for each node in the target set U
    for node in target_set:
        queue.append(node)  # add node to the end of the queue
        regions[node] = j  # set its regions to j (node is winning for j because reachability objective is satisfied)
        W.append(node)  # add the node to the winning region list of j

    if DEBUG_PRINT:
        print(g)
        print("Set " + str(target_set) + " Player " + str(j) + " Opponent " + str(opponent) + " Prio " + str(p))
        print("Marked before start " + str(regions) + " Queue before start " + str(queue))

    # while queue is not empty
    while queue:

        if DEBUG_PRINT: print("     Queue " + str(queue))

        s = queue.popleft()  # remove and return node on the left side of the queue (first in, first out)

        if DEBUG_PRINT: print("     Considering node " + str(s))

        # iterating over the predecessors of node s
        for sbis in g.get_predecessors(s):

            # get sbis info
            sbis_player = g.get_node_player(sbis)
            sbis_priority = g.get_node_priority(sbis)

            if DEBUG_PRINT:
                print("         Considering predecessor " + str(sbis) + " Is marked ? " + str(regions[sbis]) + "Player "
                      + str(sbis_player) + " Priority " + str(sbis_priority))

            # if the predecessor is in the target set
            # TODO here instead of in, we can use a special value in the regions dictionnary for nodes in target
            # set for quicker check, if so we also need to modify Wbis computation to take this into account
            if sbis in target_set:

                # if it belongs to player 1, sbis is in the attractor so we mark it
                if sbis_player == j:
                    in_att[sbis] = 1

                # else  we decrement its out value, and when it is 0 we mark it
                else:
                    out[sbis] -= 1
                    if out[sbis] == 0:
                        in_att[sbis] = 1

            if regions[sbis] == -1:  # if sbis is not yet visited, its region is -1 by default

                if sbis_player == j and sbis_priority <= p:

                    if DEBUG_PRINT: print("             Predecessor " + str(sbis) + " Added ")

                    # belongs to j, set regions and strategy accordingly
                    queue.append(sbis)
                    regions[sbis] = j
                    W.append(sbis)

                elif sbis_player == opponent and sbis_priority <= p:
                    # belongs to j bar, decrement out. If out is 0, set the region accordingly
                    out[sbis] -= 1

                    if DEBUG_PRINT: print("             Predecessor " + str(sbis) + " Decrement, new count = " +
                                          str(out[sbis]))
                    if out[sbis] == 0:

                        if DEBUG_PRINT: print("             Predecessor " + str(sbis) + " Added ")

                        queue.append(sbis)
                        regions[sbis] = j
                        W.append(sbis)

    # every node that is not marked is not in the attractor, we filter them for speed
    Wbis = filter(lambda x: regions[x] != j, g.nodes.iterkeys())

    if DEBUG_PRINT:
        print("Attractor " + str(W) + " Complement " + str(Wbis))
        print("-------------------------\n")

    return W, in_att, Wbis
Beispiel #6
0
def monotone_attractor(g, target_set, color):
    """
    Computes the monotone attractor of the target set, meaning the attractor without visiting bigger priorities than
    the one of the target set.
    :param g: a game graph.
    :param target_set: a target set of nodes.
    :param color: the color of the nodes in target set.
    :return: W, Wbis the attractor and the nodes which are not in the attractor.
    """

    priority = color  # priority of the node gives us the player for which we compute the attractor
    out = init_out(g)  # init out
    queue = deque()  # init queue (deque is part of standard library and allows O(1) append() and pop() at either end)
    # this dictionary is used to know if a node belongs to a winning region without
    # iterating over both winning regions lists (we can check in O(1) in average)
    regions = defaultdict(lambda: -1)
    W = []  # the attractor
    j = priority % 2  # the player for which we compute the attractor
    opponent = ops.opponent(j)  # player j's opponent

    for node in target_set:
        queue.append(node)  # add node to the end of the queue

    if DEBUG_PRINT:
        print("--- Monotone attractor ---")
        print(g)
        print("Set " + str(target_set) + " Player " + str(j) + " Opponent " + str(opponent) + " Prio " + str(priority))
        print("Marked before start " + str(regions) + " Queue before start " + str(queue))

    # while queue is not empty
    while queue:

        if DEBUG_PRINT: print("     Queue " + str(queue))

        s = queue.popleft()  # remove and return node on the left side of the queue (first in, first out)

        if DEBUG_PRINT: print("     Considering node " + str(s))

        # iterating over the predecessors of node s
        for sbis in g.get_predecessors(s):

            # get sbis info
            sbis_player = g.get_node_player(sbis)
            sbis_priority = g.get_node_priority(sbis)

            if DEBUG_PRINT:
                print("         Considering predecessor " + str(sbis) + " Is marked ? " + str(regions[sbis]) + "Player "
                      + str(sbis_player) + " Priority " + str(sbis_priority))

            if regions[sbis] == -1:  # if sbis is not yet visited, its region is -1 by default

                # if node is the correct player and its priority is lower or equal, add it
                if sbis_player == j and sbis_priority <= priority:

                    if DEBUG_PRINT: print("             Predecessor " + str(sbis) + " Added ")

                    # if node has not been considered yet (not already been in the queue) add it
                    # this is to avoid considering the same node twice, which can happen only for the target node and
                    # can mess up the decrementation of the counters for nodes of the opponent
                    if sbis not in target_set:
                        queue.append(sbis)

                    # mark accordingly and add to winning region
                    regions[sbis] = j
                    W.append(sbis)

                # if node is the opposite player and its priority is lower or equal, check its counter of successors
                elif sbis_player == opponent and sbis_priority <= priority:

                    # belongs to j bar, decrement out. If out is 0, set the region accordingly
                    out[sbis] -= 1

                    if DEBUG_PRINT: print("             Predecessor " + str(sbis) + " Decrement, new count = " +
                                          str(out[sbis]))

                    if out[sbis] == 0:

                        if DEBUG_PRINT: print("             Predecessor " + str(sbis) + " Added ")

                        # if node has not been considered yet (not already been in the queue) add it
                        if sbis not in target_set:
                            queue.append(sbis)

                        # mark accordingly and add to winning region
                        regions[sbis] = j
                        W.append(sbis)

    # every node that is not marked is not in the attractor, we filter them for speed
    Wbis = filter(lambda x: regions[x] != j, g.nodes.iterkeys())

    if DEBUG_PRINT:
        print("Attractor " + str(W) + " Complement " + str(Wbis))
        print("-------------------------\n")

    return W, Wbis
Beispiel #7
0
def attractor_color_vector(g, U, j, priorities):
    """
    Computes the attractor for player j of the set U in g. Does not create any strategy and only returns the set that
    corresponds to the attractor. Nodes used in the attractor cannot have a priority function p_i such that the priority
    p_i(node) of that node is larger than priorities[i].
    :param g: the game graph.
    :param U: the target set.
    :param j: the player for which we compute the attractor.
    :param priorities: vector of priorities.
    :return: W the set of nodes corresponding to the attractor.
    """
    out = init_out(g)  # init out
    queue = deque(
    )  # init queue (deque is part of standard library and allows O(1) append() and pop() at either end)
    # this dictionary is used to know if a node belongs to a winning region without
    # iterating over both winning regions lists (we can check in O(1) in average)
    regions = defaultdict(lambda: -1)
    W = []  # the attractor
    opponent = op.opponent(j)  # player j's opponent
    nbr_func = len(priorities)

    # for each node in the target set U
    for node in U:
        queue.append(node)  # add node to the end of the queue
        regions[
            node] = j  # set its regions to j (node is winning for j because reachability objective is satisfied)
        W.append(node)  # add the node to the winning region list of j
    if DEBUG_PRINT:
        print(g)
        print("Set " + str(U) + " Player " + str(j) + " Opponent " +
              str(opponent) + " Prio " + str(priorities))
        print("Marked before start " + str(regions) + " Queue before start " +
              str(queue))

    # while queue is not empty
    while queue:
        if DEBUG_PRINT: print("     Queue " + str(queue))

        s = queue.popleft(
        )  # remove and return node on the left side of the queue (first in, first out)

        if DEBUG_PRINT: print("     Considering node " + str(s))

        # iterating over the predecessors of node s
        for sbis in g.get_predecessors(s):

            sbis_player = g.get_node_player(sbis)
            # whether sbis has smaller priorities than the provided priority vector.
            # either the priorities are smaller or they are even TODO
            priority_ok = all(g.nodes[sbis][z + 1] %
                              2 == 0 or g.nodes[sbis][z + 1] <= priorities[z]
                              for z in range(nbr_func))

            if DEBUG_PRINT:
                print("         Considering predecessor " + str(sbis) +
                      " Is marked ? " + str(regions[sbis]) + "Player " +
                      str(g.get_node_player(sbis)) + " Priority " +
                      str(g.get_node_priority(sbis)))

            if regions[
                    sbis] == -1:  # if sbis is not yet visited, its region is -1 by default

                if sbis_player == j and priority_ok:

                    if DEBUG_PRINT:
                        print("             Predecessor " + str(sbis) +
                              " Added ")

                    # belongs to j, set regions and strategy accordingly
                    queue.append(sbis)

                    regions[sbis] = j
                    W.append(sbis)

                elif sbis_player == opponent and priority_ok:

                    # belongs to j bar, decrement out. If out is 0, set the region accordingly
                    out[sbis] -= 1

                    if DEBUG_PRINT:
                        print("             Predecessor " + str(sbis) +
                              " Decrement, new count = " + str(out[sbis]))
                    if out[sbis] == 0:

                        if DEBUG_PRINT:
                            print("             Predecessor " + str(sbis) +
                                  " Added ")

                        queue.append(sbis)

                        regions[sbis] = j
                        W.append(sbis)

    # every node that is not marked is not in the attractor, we filter them for speed
    Wbis = filter(lambda x: regions[x] != j, g.nodes.iterkeys())

    if DEBUG_PRINT:
        print("Attractor " + str(W) + " Complement " + str(Wbis))
        print("-------------------------\n")

    return W, Wbis
Beispiel #8
0
def attractor_color(g, U, j, p):
    """
    Computes the attractor for player j of the set U in g. Does not create any strategy and only returns the set that
    corresponds to the attractor. Nodes used in the attractor cannot have a larger priority than p.
    :param g: the game graph.
    :param U: the target set.
    :param j: the player for which we compute the attractor.
    :param p: nodes used in the computation cannot have a larger priority than p.
    :return: W the set of nodes corresponding to the attractor.
    """
    out = init_out(g)  # init out
    queue = deque(
    )  # init queue (deque is part of standard library and allows O(1) append() and pop() at either end)
    # this dictionary is used to know if a node belongs to a winning region without
    # iterating over both winning regions lists (we can check in O(1) in average)
    regions = defaultdict(lambda: -1)
    W = []  # the attractor
    opponent = op.opponent(j)  # player j's opponent

    # for each node in the target set U
    for node in U:
        queue.append(node)  # add node to the end of the queue
        regions[
            node] = j  # set its regions to j (node is winning for j because reachability objective is satisfied)
        W.append(node)  # add the node to the winning region list of j
    if DEBUG_PRINT:
        print(g)
        print("Set " + str(U) + " Player " + str(j) + " Opponent " +
              str(opponent) + " Prio " + str(p))
        print("Marked before start " + str(regions) + " Queue before start " +
              str(queue))
    # while queue is not empty
    while queue:
        if DEBUG_PRINT: print("     Queue " + str(queue))

        s = queue.popleft(
        )  # remove and return node on the left side of the queue (first in, first out)
        if DEBUG_PRINT: print("     Considering node " + str(s))
        # iterating over the predecessors of node s
        for sbis in g.get_predecessors(s):
            if DEBUG_PRINT:
                print("         Considering predecessor " + str(sbis) +
                      " Is marked ? " + str(regions[sbis]) + "Player " +
                      str(g.get_node_player(sbis)) + " Priority " +
                      str(g.get_node_priority(sbis)))
            if regions[
                    sbis] == -1:  # if sbis is not yet visited, its region is -1 by default
                if g.get_node_player(
                        sbis) == j and g.get_node_priority(sbis) <= p:
                    if DEBUG_PRINT:
                        print("             Predecessor " + str(sbis) +
                              " Added ")

                    # belongs to j, set regions and strategy accordingly
                    queue.append(sbis)
                    regions[sbis] = j
                    W.append(sbis)

                elif g.get_node_player(
                        sbis) == opponent and g.get_node_priority(sbis) <= p:
                    # belongs to j bar, decrement out. If out is 0, set the region accordingly
                    out[sbis] -= 1
                    if DEBUG_PRINT:
                        print("             Predecessor " + str(sbis) +
                              " Decrement, new count = " + str(out[sbis]))
                    if out[sbis] == 0:
                        if DEBUG_PRINT:
                            print("             Predecessor " + str(sbis) +
                                  " Added ")

                        queue.append(sbis)
                        regions[sbis] = j
                        W.append(sbis)

    Wbis = []
    for node in g.get_nodes():
        if regions[node] != j:
            Wbis.append(node)
    if DEBUG_PRINT:
        print("Attractor " + str(W) + " Complement " + str(Wbis))
        print("-------------------------\n")
    return W, Wbis
Beispiel #9
0
def zielonka_with_partial(g, partial_solver):
    """
    This is an implementation of Zielonka's recursive algorithm used to solve parity games. This implementation uses a
    provided partial solver to speed up the solving process. This implementation does not compute the winning strategies
    (for comparison purpose with other algorithms which don't).

    :param g: the game to solve.
    :param partial_solver: a partial solver.
    :return: the solution in the following format : (W_0, W_1).
    """

    rest, partial_1, partial_2 = partial_solver(
        g, [], [])  # TODO this is the default input for the algorithms
    # a more general way is to use just g as input and add the [] [] in a wrapper function in PSOLB

    W1 = []  # Winning region of player 0
    W2 = []  # Winning region of player 1

    # add the partial solutions to the winning regions
    W1.extend(partial_1)
    W2.extend(partial_2)

    # if the game is empty, return the empty regions
    if len(rest.nodes) == 0:
        return W1, W2

    else:
        i = ops.max_priority(rest)  # get max priority occurring in g

        # determining which player we are considering, if i is even : player 0 and else player 1
        if i % 2 == 0:
            j = 0
        else:
            j = 1

        opponent = ops.opponent(j)  # getting the opponent of the player

        U = ops.i_priority_node(
            rest, i)  # target set for the attractor : nodes of priority i

        # getting the attractor A and discarding the region for the opponent
        A, discard1 = attractor(rest, U, j)

        # The subgame G\A is composed of the nodes not in the attractor, thus the nodes of the opposite player's region
        G_A = rest.subgame(discard1)

        # Recursively solving the subgame G\A, solution comes as (W_0, W_1)
        sol_player1, sol_player2 = zielonka_with_partial(G_A, partial_solver)

        # depending on which player we are considering, assign regions to the proper variables
        # W'_j is noted W_j, sigma'_j is noted sig_j; the same aplies for jbar
        if j == 0:
            W_j = sol_player1
            W_jbar = sol_player2
        else:
            W_j = sol_player2
            W_jbar = sol_player1

        # if W'_jbar is empty we update the regions depending on the current player
        # the region for the whole game for one of the players is empty
        if not W_jbar:
            if j == 0:
                W1.extend(A)
                W1.extend(W_j)
            else:
                W2.extend(A)
                W2.extend(W_j)
        else:
            # compute attractor B
            B, discard1 = attractor(rest, W_jbar, opponent)
            # The subgame G\B is composed of the nodes not in the attractor, so of the opposite player's winning region
            G_B = rest.subgame(discard1)

            # recursively solve subgame G\B, solution comes as (W_0, W_1)
            sol_player1_, sol_player2_ = zielonka_with_partial(
                G_B, partial_solver)

            # depending on which player we are considering, assign regions to the proper variables
            # W''_j is noted W__j, sigma''_j is noted sig__j; the same aplies for jbar
            if j == 0:
                W__j = sol_player1_
                W__jbar = sol_player2_
            else:
                W__j = sol_player2_
                W__jbar = sol_player1_

            # the last step is to update the winning regions depending on which player we consider
            if j == 0:
                W1 = W__j

                W2.extend(W__jbar)
                W2.extend(B)

            else:
                W2 = W__j

                W1.extend(W__jbar)
                W1.extend(B)

    return W1, W2
Beispiel #10
0
def strong_parity_solver_no_strategies(g):
    """
    Strong parity games solver. This is an implementation of the recursive algorithm used to solve parity games.
    This implementation does not compute the winning strategies (for comparison purpose with other algorithms
    which don't)
    :param g: the game to solve.
    :return: the solution in the following format : (W_0, W_1).
    """
    W1 = []  # Winning region of player 0
    W2 = []  # Winning region of player 1

    # if the game is empty, return the empty regions
    if len(g.nodes) == 0:
        return W1, W2

    else:
        i = ops.max_priority(g)  # get max priority occurring in g

        # determining which player we are considering, if i is even : player 0 and else player 1
        if i % 2 == 0:
            j = 0
        else:
            j = 1

        opponent = ops.opponent(j)  # getting the opponent of the player

        U = ops.i_priority_node(
            g, i)  # target set for the attractor : nodes of priority i

        # getting the attractor A and discarding the region for the opponent
        A, discard1 = attractor(g, U, j)

        # The subgame G\A is composed of the nodes not in the attractor, thus the nodes of the opposite player's region
        G_A = g.subgame(discard1)

        # Recursively solving the subgame G\A, solution comes as (W_0, W_1)
        sol_player1, sol_player2 = strong_parity_solver_no_strategies(G_A)

        # depending on which player we are considering, assign regions to the proper variables
        # W'_j is noted W_j, sigma'_j is noted sig_j; the same aplies for jbar
        if j == 0:
            W_j = sol_player1
            W_jbar = sol_player2
        else:
            W_j = sol_player2
            W_jbar = sol_player1

        # if W'_jbar is empty we update the regions depending on the current player
        # the region for the whole game for one of the players is empty
        if not W_jbar:
            if j == 0:
                W1.extend(A)
                W1.extend(W_j)
            else:
                W2.extend(A)
                W2.extend(W_j)
        else:
            # compute attractor B
            B, discard1 = attractor(g, W_jbar, opponent)
            # The subgame G\B is composed of the nodes not in the attractor, so of the opposite player's winning region
            G_B = g.subgame(discard1)

            # recursively solve subgame G\B, solution comes as (W_0, W_1)
            sol_player1_, sol_player2_ = strong_parity_solver_no_strategies(
                G_B)

            # depending on which player we are considering, assign regions to the proper variables
            # W''_j is noted W__j, sigma''_j is noted sig__j; the same aplies for jbar
            if j == 0:
                W__j = sol_player1_
                W__jbar = sol_player2_
            else:
                W__j = sol_player2_
                W__jbar = sol_player1_

            # the last step is to update the winning regions depending on which player we consider
            if j == 0:
                W1 = W__j

                W2.extend(W__jbar)
                W2.extend(B)

            else:
                W2 = W__j

                W1.extend(W__jbar)
                W1.extend(B)

    return W1, W2
Beispiel #11
0
def zielonka_with_single_psolB_iteration(g):
    """
    This is an implementation of Zielonka's recursive algorithm used to solve parity games. This implementation uses one
    iteration of psolB, for the set of nodes of maximum priority retrieved by the algorithm. This implementation does
    not compute the winning strategies (for comparison purpose with other algorithms which don't).
    :param g: the game to solve.
    :return: the solution in the following format : (W_0, W_1).
    """

    W1 = []  # Winning region of player 0
    W2 = []  # Winning region of player 1

    W1_partial = []
    W2_partial = []

    empty_set = set()

    # if the game is empty, return the empty regions
    if len(g.nodes) == 0:
        return W1, W2

    else:
        i = ops.max_priority(g)  # get max priority occurring in g

        # determining which player we are considering, if i is even : player 0 and else player 1
        if i % 2 == 0:
            j = 0
        else:
            j = 1

        opponent = ops.opponent(j)  # getting the opponent of the player

        U = ops.i_priority_node(
            g, i)  # target set for the attractor : nodes of priority i

        rest = g  # from now on we work on the game called rest which is g by default, but can be a sub-game of g

        target_set = set(U)  # set of nodes of color 'color'

        cache = set()

        flag = True

        while flag and cache != target_set and target_set != empty_set:

            cache = target_set

            MA, discard = psolB.monotone_attractor(rest, target_set, i)

            if target_set.issubset(MA):

                att, complement = attractor(rest, MA, i % 2)

                if i % 2 == 0:
                    W1_partial.extend(att)
                else:
                    W2_partial.extend(att)

                # we have updated winning regions, we also remove attractor to obtain a sub-game
                rest = g.subgame(complement)

                if rest.get_nodes() == []:
                    return W1_partial, W2_partial

                i = ops.max_priority(rest)  # get max priority occurring in g

                # determining which player we are considering, if i is even : player 0 and else player 1
                if i % 2 == 0:
                    j = 0
                else:
                    j = 1

                opponent = ops.opponent(
                    j)  # getting the opponent of the player

                U = ops.i_priority_node(
                    rest,
                    i)  # target set for the attractor : nodes of priority i

                flag = False

            else:
                target_set = target_set.intersection(MA)

        # getting the attractor A and discarding the region for the opponent
        A, discard1 = attractor(rest, U, j)

        # The subgame G\A is composed of the nodes not in the attractor, thus the nodes of the opposite player's region
        G_A = rest.subgame(discard1)

        # Recursively solving the subgame G\A, solution comes as (W_0, W_1)
        sol_player1, sol_player2 = zielonka_with_single_psolB_iteration(G_A)

        # depending on which player we are considering, assign regions to the proper variables
        # W'_j is noted W_j, sigma'_j is noted sig_j; the same aplies for jbar
        if j == 0:
            W_j = sol_player1
            W_jbar = sol_player2
        else:
            W_j = sol_player2
            W_jbar = sol_player1

        # if W'_jbar is empty we update the regions depending on the current player
        # the region for the whole game for one of the players is empty
        if not W_jbar:
            if j == 0:
                W1.extend(A)
                W1.extend(W_j)
            else:
                W2.extend(A)
                W2.extend(W_j)
        else:
            # compute attractor B
            B, discard1 = attractor(rest, W_jbar, opponent)
            # The subgame G\B is composed of the nodes not in the attractor, so of the opposite player's winning region
            G_B = rest.subgame(discard1)

            # recursively solve subgame G\B, solution comes as (W_0, W_1)
            sol_player1_, sol_player2_ = zielonka_with_single_psolB_iteration(
                G_B)

            # depending on which player we are considering, assign regions to the proper variables
            # W''_j is noted W__j, sigma''_j is noted sig__j; the same aplies for jbar
            if j == 0:
                W__j = sol_player1_
                W__jbar = sol_player2_
            else:
                W__j = sol_player2_
                W__jbar = sol_player1_

            # the last step is to update the winning regions depending on which player we consider
            if j == 0:
                W1 = W__j

                W2.extend(W__jbar)
                W2.extend(B)

            else:
                W2 = W__j

                W1.extend(W__jbar)
                W1.extend(B)

    if not flag:
        W1.extend(W1_partial)
        W2.extend(W2_partial)

    return W1, W2
Beispiel #12
0
def monotone_attractor_player0(g, node, priorities, player):
    """
    Computes an attractor to a single node while making sure that no priority greater than that of the target node
    is visited (for every priority functions).
    :param g: a game graph.
    :param node: a target node.
    :param priorities: the informations regarding that node : (player, priority_1, ..., priority_k).
    :param player: the player for which we compute the attractor.
    :return: the monotone attractor of node in g.
    """

    nbr_func = len(priorities) - 1  # number of priority functions
    out = init_out(g)  # init out
    queue = deque(
    )  # init queue (deque is part of standard library and allows O(1) append() and pop() at either end)
    # this dictionary is used to know if a node belongs to a winning region without
    # iterating over both winning regions lists (we can check in O(1) in average)
    regions = defaultdict(lambda: -1)
    W = []  # the attractor
    j = player  # the player for which we compute the attractor
    opponent = op.opponent(j)  # player j's opponent

    queue.append(node)  # add node to the end of the queue.
    # Note: node is not de facto in the attractor, it will be added during computations if it is

    if DEBUG_PRINT:
        print("--- Monotone attractor ---")
        print(g)
        print("Node " + str(node) + " Player " + str(j) + " Opponent " +
              str(opponent) + " Priority " + str(priorities))
        print("Marked before start " + str(regions) + " Queue before start " +
              str(queue))

    # while queue is not empty
    while queue:

        if DEBUG_PRINT: print("     Queue " + str(queue))

        s = queue.popleft(
        )  # remove and return node on the left side of the queue (first in, first out)

        if DEBUG_PRINT: print("     Considering node " + str(s))

        # iterating over the predecessors of node s
        for sbis in g.get_predecessors(s):

            sbis_priority = g.get_node_priority(sbis)
            sbis_player = g.get_node_player(sbis)

            if DEBUG_PRINT:
                print("         Considering predecessor " + str(sbis) +
                      " Is marked ? " + str(regions[sbis]) + " Player " +
                      str(sbis_player) + " Priority " + str(sbis_priority))

            if regions[
                    sbis] == -1:  # if sbis is not yet visited, its region is -1 by default

                # in any case, we only consider predecessors which have smaller priorities according to every function
                flag = False  # records wether a priority function i has been found such that alpha_i(sbis) > alpha_i(s)

                # functions are from 1 to k
                for index in range(1, nbr_func + 1):

                    # priority of sbis according to function index
                    prio = g.get_node_priority_function_i(sbis, index)

                    # TODO add prio % 2 != player and here if we are to consider the extended condition on priorities

                    # if the priority of sbis is greater, we break because we have found a function which falsifies
                    if prio > priorities[index]:
                        flag = True
                        break

                # if the node is not to be considered, continue to the next iteration of the loop, else check the player
                if flag: continue

                # if node is the correct player (its priority is lower or equal per the above check)
                if sbis_player == j:

                    if DEBUG_PRINT:
                        print("             Predecessor " + str(sbis) +
                              " Added ")

                    # if node has not been considered yet (not already been in the queue) add it
                    # this is to avoid considering the same node twice, which can happen only for the target node and
                    # can mess up the decrementation of the counters for nodes of the opponent
                    if sbis != node:
                        queue.append(sbis)

                    # mark accordingly and add to winning region
                    regions[sbis] = j
                    W.append(sbis)

                # if node is the opposite player, check its counter of successors
                elif sbis_player == opponent:

                    # belongs to opponent, decrement out. If out is 0, set the region accordingly
                    out[sbis] -= 1

                    if DEBUG_PRINT:
                        print("             Predecessor " + str(sbis) +
                              " Decrement, new count = " + str(out[sbis]))

                    if out[sbis] == 0:

                        if DEBUG_PRINT:
                            print("             Predecessor " + str(sbis) +
                                  " Added ")

                        # if node has not been considered yet (not already been in the queue) add it
                        if sbis != node:
                            queue.append(sbis)

                        # mark accordingly and add to winning region
                        regions[sbis] = j
                        W.append(sbis)

    Wbis = filter(lambda x: regions[x] != j, g.nodes.iterkeys())

    if DEBUG_PRINT:
        print("Attractor " + str(W) + " Complement " + str(Wbis))
        print("-------------------------\n")

    return W, Wbis