import itertools
compose = lambda f: lambda g: lambda *a, **k: f(g(*a, **k))

def downward_closure_paths(path, edges):
    found = {}


def downward_closure(supervertex, edges):
    return downward_closure_paths({supervertex: ()}, edges)


edges = [
    (1, 2, 3),
    (4, 5),
    (2, 4),
    (3, 5, 6),
]
'''
1   2
 \ / \
  v   \
  |    4
  3    |
   \   5
    \ /
     v
     |
     6
'''

def simple_upward_closure(vertices, flattened_edges):
    found = frozenset(a for (a, b) in flattened_edges if b in vertices)
    if found.issubset(vertices):
        return vertices
    return simple_upward_closure(found | vertices, flattened_edges)

def common_roots(vertices, flattened_edges):
    return frozenset.intersection(
            *(simple_upward_closure(frozenset((frozenset((v,)),)), flattened_edges) for v in vertices))



@compose(frozenset)
def flat_edges(edges):
    tail_vertices, head_vertices = [], []
    for *tails, head in edges:
        tail_vertices.append(frozenset(tails))
        head_vertices.append(frozenset((head,)))
    for tailset, headset in zip(tail_vertices, head_vertices):
        yield tailset, headset
        for vertexset in tail_vertices + head_vertices:
            if vertexset.issubset(tailset):
                yield tailset, vertexset

@compose(frozenset)
def all_edges(edges):
    flattened = flat_edges(edges)
    yield from flattened
    for a, b in flattened:
        if len(b) > 1:
            for root in common_roots(a, flattened):
                yield root, b


for e in flat_edges(edges):
    print(e)

print("\n\n\n")
for e in all_edges(edges) - flat_edges(edges):
    print(e)
#assert(downward_closure({2}, edges) == {2: (), 4: ((2,4),), 5:

import itertools
compose = lambda f: lambda g: lambda *a, **k: f(g(*a, **k))

def downward_closure_paths(path, edges):
    found = {}


def downward_closure(supervertex, edges):
    return downward_closure_paths({supervertex: ()}, edges)


edges = [
    (1, 2, 3),
    (4, 5),
    (2, 4),
    (3, 5, 6),
]
'''
1   2
 \ / \
  v   \
  |    4
  3    |
   \   5
    \ /
     v
     |
     6
'''

def simple_upward_closure(vertices, flattened_edges):
    found = frozenset(a for (a, b) in flattened_edges if b in vertices)
    if found.issubset(vertices):
        return vertices
    return simple_upward_closure(found | vertices, flattened_edges)

def common_roots(vertices, flattened_edges):
    return frozenset.intersection(
            *(simple_upward_closure(frozenset((frozenset((v,)),)), flattened_edges) for v in vertices))



@compose(frozenset)
def flat_edges(edges):
    tail_vertices, head_vertices = [], []
    for *tails, head in edges:
        tail_vertices.append(frozenset(tails))
        head_vertices.append(frozenset((head,)))
    for tailset, headset in zip(tail_vertices, head_vertices):
        yield tailset, headset
        for vertexset in tail_vertices + head_vertices:
            if vertexset.issubset(tailset):
                yield tailset, vertexset

@compose(frozenset)
def all_edges(edges):
    flattened = flat_edges(edges)
    yield from flattened
    for a, b in flattened:
        for root in common_roots(a, flattened):
            yield root, b


for e in flat_edges(edges):
    print(e)

print("\n\n\n")
for e in all_edges(edges) - flat_edges(edges):
    print(e)
#assert(downward_closure({2}, edges) == {2: (), 4: ((2,4),), 5:

Revert to working downward closure