2

I don't know the proper name for this connected component, yet what I'm looking for is to form paths with all possible combinations of a list of pairs/edges. Given a list:

("A", "B")
("A", "C")
("A", "D")
("C", "D")
("D", "B")
("D", "E")
("E", "F")

I would like my output similar to this:

("A", "B")
("B", "A")
("A", "C")
("C", "A")
("A", "D")
("D", "A")
("C", "D")
("D", "C")
...
("A", "B", "D")
("A", "C", "D")
("A", "D", "B")
("A", "D", "C")
("A", "D", "E")
("B", "A", "C")
("B", "A", "D")
("B", "D", "A")
("B", "D", "C")
("B", "D", "E")
...
("A", "B", "D", "E")
("A", "C", "D", "E")
...
("A", "B", "D", "E", "F")
...
("F", "E", "D", "B", "A")

I will need no more than four to five combinations (depending on how large is the output).

Vaguely related to what I'm looking for, I found in more_itertools.distinct_permutations and networkx.algorithms.clique.enumerate_all_cliques

admirabilis
  • 2,290
  • 2
  • 18
  • 33
  • can you show the code you have tried – deadshot Mar 28 '22 at 07:31
  • @deadshot So far, I have mostly researched some algorithm that would match this problem. enumerate_all_cliques, for example, gives some of desired combinations, but not all. – admirabilis Mar 28 '22 at 07:40
  • 1
    Do you want edges or vertices to be repeated several times in some combinations? For example ABCAD repeats vertex A twice, and ABCABD repeats edge AB twice. Do you allow both cases? – Arty Mar 28 '22 at 07:43
  • @Arty no, there can be no repetitions. ABCAD can't happen, and ABCD is invalid, since there is no BC pair. – admirabilis Mar 28 '22 at 07:47
  • @admirabilis I provided just imaginary case when this can happen. I'm not saying that my two repeated paths are formed out of your inputs. So in imaginary case if it CAN happen is it allowed to have repeated edges or vertices? Because you have Edges of some graph, these edges fully represent a graph. And different paths in ANY graph can have repeated vertices or repeated edges, it is quite normal to show path that have at least repeated vertices. Imagine graph in a form of hourglass, if the task of Path to start from first vertex and come back to it, then you have to visit twice central vertex – Arty Mar 28 '22 at 07:51
  • @Arty if it happens that the algorithm returns repeated paths, I will have to filter them out. From the output, I will select all outputs starting with "A" or ending with "A", or "B", etc., as necessary. Note: and I'm sorry that the example above had an invalid path that I now removed: AED – admirabilis Mar 28 '22 at 07:56
  • @MisterMiyagi no, I just need paths in the form of tuples/lists. My search got me to https://stackoverflow.com/a/55828014/756589 that made me believe NetworkX could help with this particular issue. – admirabilis Mar 28 '22 at 08:07

1 Answers1

1

Your task can be easily solved through regular Depth First Search approach of Graph traversal using Recursive function.

My code below fully implements this approach. Just call all_paths() function with given list of edges and it will return all possible paths up to desired maximal length.

This function has some extra params allow_same_vertices = False, allow_same_edges = False, max_length = 5. First param says if it is allowed to repeat same vertices twice within single path. Second says if it is allowed to repeat same edge twice (sometimes it might be allowed to repeat same vertex but not same edge). Third param says what is allowed maximal length of the path.

How this Depth First Search works: Recursive function starts with some vertex, then it tries to visit all neighbours of this vertex (by following all edges from current vertex), if neighour is present in set of visited vertices then this neighour is skipped, otherwise neighbour is added to visited vertices and added to the path and recursive function is called again with this neighbour as argument. After recursive call is finished, neighbour is removed from path and from visited vertices.

This kind of recursive depth first search traversal of a graph is guaranteed to visit ALL possible paths, it is a well known algorithm.

Try it online!

def all_paths(edges, *, allow_same_vertices = False, allow_same_edges = False, max_length = 5):
    neighbours = {None: []}
    for a, b in edges:
        for i in range(2):
            if a not in neighbours[None]:
                neighbours[None].append(a)
            if a not in neighbours:
                neighbours[a] = []
            if b not in neighbours[a]:
                neighbours[a].append(b)
            a, b = b, a
    visited_edges = {}
    visited_vertices = {}
    paths = set()
    path = []
    def rec(vertex):
        if len(path) >= 2:
            paths.add(tuple(path))
        if len(path) >= max_length:
            return
        for neighbour in neighbours.get(vertex, []):
            if not allow_same_vertices and visited_vertices.get(neighbour, 0) > 0:
                continue
            if not allow_same_edges and visited_edges.get((vertex, neighbour), 0) > 0:
                continue
            visited_vertices[neighbour] = visited_vertices.get(neighbour, 0) + 1
            visited_edges[(vertex, neighbour)] = visited_edges.get((vertex, neighbour), 0) + 1
            path.append(neighbour)
            rec(neighbour)
            path.pop()
            visited_vertices[neighbour] -= 1
            visited_edges[(vertex, neighbour)] -= 1
    rec(None)
    return sorted(paths, key = lambda e: (len(e), e))

def main():
    for path in all_paths([
        ("A", "B"),
        ("A", "C"),
        ("A", "D"),
        ("C", "D"),
        ("D", "B"),
        ("D", "E"),
        ("E", "F"),
    ], max_length = 4):
        print(path)

main()

Input:

("A", "B"),
("A", "C"),
("A", "D"),
("C", "D"),
("D", "B"),
("D", "E"),
("E", "F"),

Output:

('A', 'B')
('A', 'C')
('A', 'D')
('B', 'A')
('B', 'D')
('C', 'A')
('C', 'D')
('D', 'A')
('D', 'B')
('D', 'C')
('D', 'E')
('E', 'D')
('E', 'F')
('F', 'E')
('A', 'B', 'D')
('A', 'C', 'D')
('A', 'D', 'B')
('A', 'D', 'C')
('A', 'D', 'E')
('B', 'A', 'C')
('B', 'A', 'D')
('B', 'D', 'A')
('B', 'D', 'C')
('B', 'D', 'E')
('C', 'A', 'B')
('C', 'A', 'D')
('C', 'D', 'A')
('C', 'D', 'B')
('C', 'D', 'E')
('D', 'A', 'B')
('D', 'A', 'C')
('D', 'B', 'A')
('D', 'C', 'A')
('D', 'E', 'F')
('E', 'D', 'A')
('E', 'D', 'B')
('E', 'D', 'C')
('F', 'E', 'D')
('A', 'B', 'D', 'C')
('A', 'B', 'D', 'E')
('A', 'C', 'D', 'B')
('A', 'C', 'D', 'E')
('A', 'D', 'E', 'F')
('B', 'A', 'C', 'D')
('B', 'A', 'D', 'C')
('B', 'A', 'D', 'E')
('B', 'D', 'A', 'C')
('B', 'D', 'C', 'A')
('B', 'D', 'E', 'F')
('C', 'A', 'B', 'D')
('C', 'A', 'D', 'B')
('C', 'A', 'D', 'E')
('C', 'D', 'A', 'B')
('C', 'D', 'B', 'A')
('C', 'D', 'E', 'F')
('D', 'B', 'A', 'C')
('D', 'C', 'A', 'B')
('E', 'D', 'A', 'B')
('E', 'D', 'A', 'C')
('E', 'D', 'B', 'A')
('E', 'D', 'C', 'A')
('F', 'E', 'D', 'A')
('F', 'E', 'D', 'B')
('F', 'E', 'D', 'C')
Arty
  • 14,883
  • 6
  • 36
  • 69