6

I have an optimization problem and I am looking for an algorithm that performs better than the naive method I have.

The problem

Consider a graph, G(N, E), where N is the set of nodes and E is the set of edges. Each node n_i in N has an associated state s_i. I've used a dictionary D to store this with nodes as keys and the corresponding state as the value. Adjacent nodes may swap states using a swap manouever. Specifically, a swap manouever is defined as

def swap(G, D, n_i, n_j):
    if n_i not in neighbors(G, n_j):
         print('Cannot swap between disconnected nodes')
    else:
         s_i = D[n_i]
         s_j = D[n_j]
         D[n_i] = s_j
         D[n_j] = s_i
    return D

Each state must now "visit" a list of other states in a given order, where visiting means both states must be on any pair of adjacent nodes on G(N, E). For example s_1 must visit [s_3, s_5, s_2], s_2 must visit [s_1, s_3], s_3 must visit [s_1, s_2, s_4] and so on. The order of visits must be preserved. However, states are allowed to be on adjacent nodes without marking that as a visit. For instance, if s_1 starts off adjacent to s_5, then it must first become adjacent to s_3, mark that visit as done and then become adjacent to s_5 again. The visitor lists are such that if s_i must visit s_j, then that also means that s_j must visit s_i i.e. everything is self-consistent.

Typical sizes

The graph is usually around 20-30 nodes, each node is typically connected to 2-4 others. The visit list has no length restriction in principle but in practice doesn't exceed 10.

The goal

The goal is to complete the list of visits while minimizing the total number of swap manouvers. What is an efficient algorithm/heuristic for this problem?

My naive solution

My current algorithm does the simplest thing one can do. I first see which vists are (0,0) visits according to the following function i.e. both states must visit each other before visiting anyone else.

def interaction_priority(s1, s2):
    if s2 in visitors_of_s1:
       b = visitors_of_s1.index(s2)
       a = visitors_of_s2.index(s1)
    return (a, b)

For these states, I get the current nodes and find the shortest path between those nodes on G using a networx function. I perform swaps till they are adjacent. Let us assume the previous function returned (0,0) for s_1 and s_3. Then I find the shortest path between the current nodes of s_1 and s_3

inv_D = {s : n for n, s in D.items()} #Invert the dictionary of nodes and states
def get_shortest_path(s1, s3):
    n1 = inv_D[s1]
    n3 = inv_D[s3]
    path_nodes = get_shortest_path(G, n1, n3)
    return path_nodes

Now, I perform swaps along this path

if len(path_nodes)> 0:
    for i in path_nodes:
         D = swap(G, D, n1, i)

visitors_of_s1.remove(s3)
visitors_of_s3.remove(s1)

I'm aware that I can probably optimize things a bit e.g. make the last method symmetric and so on but the fundamental idea is still not very clever. Is there a cleverer algorithm that optimizes how the states move about the graph but still complete all their visits.

user1936752
  • 756
  • 1
  • 9
  • 25
  • 1
    In your naive solution, don't you also need to avoid being adjacent with other states during the sequence of swapping? Sorry if I misunderstood the problem. – justhalf Nov 22 '19 at 20:33
  • @justhalf, good point - I forgot to add that. You can be adjacent without visiting but to visit, you must be adjacent. – user1936752 Nov 22 '19 at 22:04
  • Can you characterize the size and nature of the datasets? Specifically, how large are the graphs, what percentage of states have a required visit list, and how long are the visit-lists relative to the total number of states, typically? Since this may be an O(2^n) or worse problem, it’s very important to know things like this for optimization strategies. – RBarryYoung Nov 28 '19 at 21:56
  • 1
    @RBarryYoung typically around 20-30 nodes, each node is typically connected to 2-4 others. The visit list has no length restriction in principle but in practice under 10. – user1936752 Nov 29 '19 at 00:02

0 Answers0