Пример #1
0
def lattice(iter_or_int: IterOrInt = None,
            *,
            width: Optional[int] = None,
            height: Optional[int] = None) -> Graph:
    if iter_or_int is None:
        if width is None or height is None:
            raise ValueError(
                'if iter_or_int is None, width and height must be provided.'
            )

        iter_or_int = width * height

    vertices = list(_int_to_range(iter_or_int))

    if len(vertices) == 0:
        raise ValueError(
            'iterable is empty'
        )

    width, height = _discover_width_and_height(
        len(vertices), width, height
    )

    matrix = [vertices[i:i + width] for i in range(0, len(vertices), width)]

    transposed = [list(z) for z in zip(*matrix)]

    edges = _parallel_edges(matrix) | _parallel_edges(transposed)

    return Graph(
        vertices,
        edges
    )
Пример #2
0
def _dijkstra(
    g: Graph,
    start: Vertex,
) -> Tuple[Dict[Vertex, float], Dict[Vertex, Optional[Vertex]]]:

    distance = {v: inf for v in g.vertices}
    distance[start] = 0

    previous: Dict[Vertex, Optional[Vertex]]
    previous = {v: None for v in g.vertices}

    unvisited: Set[Vertex] = g.vertices

    while len(unvisited) > 0:
        (current, _) = min({(v, distance[v])
                            for v in unvisited},
                           key=lambda t: t[1])

        unvisited.remove(current)

        for n in g.neighbors(current):
            distn = distance[current] + g.weight[current, n]

            if distn < distance[n]:
                distance[n] = distn
                previous[n] = current

    return distance, previous
Пример #3
0
def biclique(iter_or_int1: IterOrInt, iter_or_int2: IterOrInt) -> Graph:
    vertices1 = set(_int_to_range(iter_or_int1))
    vertices2 = set(_int_to_range(iter_or_int2))

    return Graph(
        vertices1 | vertices2,
        product(vertices1, vertices2)
    )
Пример #4
0
def prim(g: Graph) -> Graph:
    """
    Constructs a minimum spanning tree using Prim's algorithm
    """
    cost: Dict[Vertex, float] = {v: inf for v in g.vertices}
    edge: Dict[Vertex, Vertex] = {v: None for v in g.vertices}

    tree = Graph()

    vertices_left = g.vertices

    while vertices_left:
        # find the vertex in the fringe with the minimal cost
        (current_vertex, current_cost) = min(
            cost.items(),
            key=lambda t: t[1],
        )

        for v in g.neighbors(current_vertex):
            if v in cost and g.weight[v, current_vertex] < cost[v]:
                cost[v] = g.weight[v, current_vertex]
                edge[v] = current_vertex

        tree.insert(current_vertex)

        if current_cost != inf:
            tree.link(current_vertex, edge[current_vertex],
                      cast(int, current_cost))

        del edge[current_vertex]
        del cost[current_vertex]
        vertices_left.remove(current_vertex)

    return tree
Пример #5
0
def complete(iter_or_int: IterOrInt) -> Graph:
    vertices = set(_int_to_range(iter_or_int))

    return Graph(
        vertices,
        {
            (min(v1, v2), max(v1, v2))
            for v1, v2 in product(vertices, vertices)
            if v1 is not v2
        }
    )
Пример #6
0
def binary_tree(iter_or_int: IterOrInt) -> Graph:
    vertices = list(_int_to_range(iter_or_int))

    if len(vertices) == 0:
        raise ValueError(
            'tree cannot be empty'
        )

    g = Graph(vertices)

    for i, _ in enumerate(vertices):
        for offset in {1, 2}:
            j = i * 2 + offset

            if j < len(vertices):
                g.link(
                    vertices[i],
                    vertices[j],
                )

    return g
Пример #7
0
def coloring(g: Graph) -> Dict[Vertex, int]:
    colors: Dict[Vertex, int] = {}

    for v in g.vertices:
        available = [True] * g.order

        for adj in g.neighbors(v):
            if colors.get(adj) is not None:
                available[colors[adj]] = False

        colors[v] = available.index(True)

    return colors
Пример #8
0
def fringe(g: Graph, selected: Iterable[Vertex]) -> Set[Vertex]:
    selected = set(selected)

    if not selected.issubset(g.vertices):
        raise ValueError("selected is not a subset of the graph's vertices")

    fr = set()

    for v in selected:
        for v2 in g.neighbors(v):
            if v2 not in selected:
                fr.add(v2)

    return fr
Пример #9
0
def transitive_closure(g: Graph,
                       v: Vertex,
                       visited: Optional[Set[Vertex]] = None) -> Set[Vertex]:
    """
    Returns a set containing all vertices reachable from v
    """
    visited = visited or set()

    visited.add(v)

    for v_neigh in g.neighbors(v):
        if v_neigh not in visited:
            transitive_closure(g, v_neigh, visited)

    return visited
Пример #10
0
def kruskal(g: Graph) -> Graph:
    """
    Constructs a minimum spanning tree using Kruskal's algorithm

    The input graph must not have parallel edges or loops
    """
    for v1, v2, _ in g.edges:
        if v1 == v2:
            raise ValueError("g can't have loops")

    edges = sorted(g.edges, key=lambda e: e[2], reverse=True)

    t = Graph(g.vertices)

    n_edges = 0

    while t.order - 1 > n_edges:
        v1, v2, w = edges.pop()

        if v1 not in transitive_closure(t, v2):
            t.link(v1, v2, w)
            n_edges += 1

    return t
Пример #11
0
def bfs(g: Graph, start: Vertex, condition: Test) -> Optional[Vertex]:

    deq: Deque[Vertex] = deque()
    visited = set()

    deq.append(start)
    visited.add(start)

    while len(deq) > 0:
        cur = deq.popleft()

        if condition(cur):
            return cur

        for n in g.neighbors(cur):
            if n not in visited:
                deq.append(n)
                visited.add(n)

    return None
Пример #12
0
def to_dot(
    g: Graph,
    to_str: VertexToString = str,
    force_weight: bool = False,
    edge_color: EdgeToColor = None
) -> str:
    edge_color = edge_color or (lambda *args: None)

    dot = ['graph {\n']

    for v1, v2, w in g.edges:
        dot += ['"', to_str(v1), '" -- "', to_str(v2), '"']

        opts = []

        if w != 1 or force_weight:
            opts.append(f'label="{str(w)}"')

        color = edge_color(v1, v2, w)

        if color is not None:
            opts.append(f'color="{color}"')

        if len(opts) > 0:
            dot += [
                ' [',
                *opts,
                ']'
            ]

        dot.append(';\n')

    for v in g.vertices:
        if len(g.neighbors(v)) == 0:
            dot += ['"', to_str(v), '";\n']

    dot.append('}')

    return ''.join(dot)
Пример #13
0
def dfs(g: Graph,
        current: Vertex,
        condition: Test,
        visited: Set = None) -> Optional[Vertex]:

    visited = visited or set()

    if current in visited:
        return None

    visited.add(current)

    if condition(current):
        return current

    for n in g.neighbors(current):
        v = dfs(g, n, condition, visited)

        if v is not None:
            return v

    return None
Пример #14
0
def _cycle_with(g: Graph,
                v: Vertex,
                v_prev: Vertex,
                visited: Set[Vertex] = None) -> bool:
    """
    Returns True if there is a cycle in the graph containing v,
    False otherwise
    """
    visited = visited or set()

    if v in visited:
        return True

    visited.add(v)

    for v_neigh in g.neighbors(v):
        if v_neigh != v_prev:
            if _cycle_with(g, v_neigh, v, visited):
                return True

    visited.remove(v)

    return False
Пример #15
0
def hamiltonian_cycle(g: Graph, start: Vertex) -> List[Vertex]:
    path = [start]

    current = start

    visited: Set[Vertex] = set()

    try:
        while len(visited) != g.order:
            visited.add(current)

            (_, nearest) = min((g.weight[current, v], v)
                               for v in g.neighbors(current)
                               if v not in visited)

            path.append(nearest)

            current = nearest
    except ValueError as e:
        if len(path) == g.order:
            return path

    raise HamiltonianCycleNotFound('graph has dead ends')
Пример #16
0
def floyd_warshall(g: Graph) -> Dict[Vertex, Dict[Vertex, float]]:
    dist: Dict[Vertex, Dict[Vertex, float]] = {}

    vertices = g.vertices

    for v1 in vertices:
        dist[v1] = {}

        for v2 in vertices:
            if v1 is v2:
                dist[v1][v2] = 0
            elif g.has_edge(v1, v2):
                dist[v1][v2] = g.weight[v1, v2]
            else:
                dist[v1][v2] = inf

    for k in vertices:
        for i in vertices:
            for j in vertices:
                if dist[i][j] > dist[i][k] + dist[k][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]

    return dist