Skip to content

Repository files navigation

AlgoGraph

Immutable graph data structures and algorithms library with functional transformers, declarative selectors, and lazy views.

PyPI versionPython 3.8+License: MIT

Installation

pip install AlgoGraph

Overview

AlgoGraph provides immutable graph data structures with a clean, functional API. Version 2.0.0 brings AlgoTree-level API elegance with pipe-based transformers, declarative selectors, and lazy views—achieving ~90% code reduction for common operations.

Key Features

  • Immutable by default: All operations return new graph objects
  • 56+ algorithms: Traversal, shortest path, centrality, flow, matching, coloring
  • Pipe-based transformers: Composable operations with | operator (v2.0.0)
  • Declarative selectors: Pattern matching with logical operators (v2.0.0)
  • Lazy views: Memory-efficient filtering without copying (v2.0.0)
  • Fluent builder API: Construct graphs with 82% less code
  • Type-safe: Full type hints for IDE support
  • Optional AlgoTree integration: Convert between trees and graphs

Quick Start

fromAlgoGraphimportGraph, Vertex, Edge# Create a graphg= (Graph.builder()
.add_vertex('A', age=30)
.add_vertex('B', age=25)
.add_edge('A', 'B', weight=5.0)
.build())
# Query the graphg.has_edge('A', 'B') # Trueg.neighbors('A') # {'B'}# Run algorithmsfromAlgoGraph.algorithmsimportdijkstra, pagerankdistances=dijkstra(g, source='A')

Advanced Features (v2.0.0)

Transformer Pipelines

Compose graph operations using the | pipe operator:

fromAlgoGraph.transformersimportfilter_vertices, largest_component, stats# Filter → extract component → compute statsresult= (graph|filter_vertices(lambdav: v.get('active'))
|largest_component()
|stats())
# result: {'vertex_count': 42, 'edge_count': 156, 'density': 0.18, ...}

Available Transformers:

  • filter_vertices(pred), filter_edges(pred) - Filter by predicate
  • map_vertices(fn), map_edges(fn) - Transform attributes
  • reverse(), to_undirected() - Structure transformations
  • largest_component(), minimum_spanning_tree() - Algorithm-based
  • to_dict(), to_adjacency_list(), stats() - Export operations

Declarative Selectors

Query vertices and edges with logical operators:

fromAlgoGraph.graph_selectorsimportvertexasv, edgease# Find active users with high degreepower_users=graph.select_vertices(
v.attrs(active=True) &v.degree(min_degree=10)
)
# Find heavy edges from admin nodesadmin_edges=graph.select_edges(
e.source(v.attrs(role='admin')) &e.weight(min_weight=100)
)
# Complex queries with OR, NOT, XORspecial=graph.select_vertices(
(v.attrs(vip=True) |v.degree(min_degree=50)) &~v.attrs(banned=True)
)

Selector Types:

  • vertex.id(pattern) - Match by ID (glob/regex)
  • vertex.attrs(**attrs) - Match attributes (supports callables)
  • vertex.degree(min/max/exact) - Match by degree
  • edge.weight(min/max/exact) - Match by weight
  • edge.source(selector), edge.target(selector) - Match endpoints

Lazy Views

Efficient filtering without copying data:

fromAlgoGraph.viewsimportfiltered_view, neighborhood_view# Create view without copyingview=filtered_view(
large_graph,
vertex_filter=lambdav: v.get('active'),
edge_filter=lambdae: e.weight>5.0
)
# Iterate lazilyforvertexinview.vertices():
process(vertex)
# Materialize only when neededsmall_graph=view.materialize()
# Explore k-hop neighborhoodlocal=neighborhood_view(graph, center='Alice', k=2)

View Types:

  • filtered_view() - Filter vertices/edges
  • subgraph_view() - View specific vertices
  • reversed_view() - Reverse edge directions
  • undirected_view() - View as undirected
  • neighborhood_view() - k-hop neighborhood

Core Classes

Vertex

fromAlgoGraphimportVertexv=Vertex('A', attrs={'value': 10, 'color': 'red'})
v2=v.with_attrs(value=20) # Immutable updatev3=v.without_attrs('color') # Remove attribute

Edge

fromAlgoGraphimportEdgee=Edge('A', 'B', directed=True, weight=5.0)
e2=e.with_weight(10.0) # Immutable updatee3=e.reversed() # B -> A

Graph

fromAlgoGraphimportGraph, Vertex, Edge# Direct constructiong=Graph({Vertex('A'), Vertex('B')}, {Edge('A', 'B')})
# Fluent builderg= (Graph.builder()
.add_vertex('A', age=30)
.add_edge('A', 'B', weight=5.0)
.add_path('B', 'C', 'D')
.add_cycle('X', 'Y', 'Z')
.build())
# From edges (auto-creates vertices)g=Graph.from_edges(('A', 'B'), ('B', 'C'), ('C', 'A'))
# Immutable operationsg2=g.add_vertex(Vertex('D'))
g3=g.remove_vertex('A')
g4=g.subgraph({'B', 'C', 'D'})

Algorithms

Traversal

fromAlgoGraph.algorithmsimportdfs, bfs, topological_sort, has_cycle, find_pathvisited=dfs(graph, start_vertex='A')
levels=bfs(graph, start_vertex='A')
order=topological_sort(graph) # DAG onlypath=find_path(graph, 'A', 'Z')

Shortest Paths

fromAlgoGraph.algorithmsimportdijkstra, bellman_ford, floyd_warshall, a_stardistances=dijkstra(graph, source='A')
distances=bellman_ford(graph, source='A') # Handles negative weightsall_pairs=floyd_warshall(graph)
path=a_star(graph, 'A', 'Z', heuristic=h)

Connectivity

fromAlgoGraph.algorithmsimport (
connected_components, strongly_connected_components,
is_connected, is_bipartite, find_bridges, find_articulation_points
)
components=connected_components(graph)
scc=strongly_connected_components(graph)
bridges=find_bridges(graph)
articulation=find_articulation_points(graph)

Spanning Trees

fromAlgoGraph.algorithmsimportminimum_spanning_tree, kruskal, primmst=minimum_spanning_tree(graph)
mst=kruskal(graph)
mst=prim(graph, start='A')

Centrality

fromAlgoGraph.algorithmsimport (
pagerank, betweenness_centrality, closeness_centrality,
degree_centrality, eigenvector_centrality
)
pr=pagerank(social_network)
bc=betweenness_centrality(network)
cc=closeness_centrality(network)

Flow Networks

fromAlgoGraph.algorithmsimportmax_flow, min_cut, edmonds_karpflow_value=max_flow(network, 'Source', 'Sink')
cut_value, source_set, sink_set=min_cut(network, 'Source', 'Sink')

Matching

fromAlgoGraph.algorithmsimporthopcroft_karp, maximum_bipartite_matching, is_perfect_matchingmatching=hopcroft_karp(bipartite_graph, left_set, right_set)
max_matching=maximum_bipartite_matching(graph, left, right)

Graph Coloring

fromAlgoGraph.algorithmsimportwelsh_powell, chromatic_number, dsatur, is_k_colorablecoloring=welsh_powell(graph)
num_colors=chromatic_number(graph)
coloring=dsatur(graph) # Often better than greedy

Serialization

fromAlgoGraphimportsave_graph, load_graph# Save/load JSONsave_graph(graph, 'network.json')
graph=load_graph('network.json')

AlgoTree Integration (Optional)

fromAlgoTreeimportNode, TreefromAlgoGraphimporttree_to_graph, graph_to_tree# Tree to Graphtree=Tree(Node('root', Node('child1'), Node('child2')))
graph=tree_to_graph(tree)
# Graph to Tree (extracts spanning tree)tree=graph_to_tree(graph, root='root')

Interactive Shell

Explore graphs with a filesystem-like interface:

pip install AlgoGraph
algograph # Start with sample graph
algograph network.json # Load from file
graph(5v):/$ ls
Alice/ [2 neighbors]
Bob/ [3 neighbors]
graph(5v):/$ cd Alice
graph(5v):/Alice$ ls
Attributes:
age = 30
neighbors/ [2 vertices]
graph(5v):/Alice$ path Bob Eve
Path found: Alice -> Bob -> Diana -> Eve

Examples

Social Network Analysis

fromAlgoGraphimportGraphfromAlgoGraph.algorithmsimportpagerank, betweenness_centralityfromAlgoGraph.transformersimportfilter_vertices, statsfromAlgoGraph.graph_selectorsimportvertexasv# Build networknetwork= (Graph.builder()
.add_vertex('Alice', followers=1000)
.add_vertex('Bob', followers=500)
.add_vertex('Charlie', followers=2000)
.add_edge('Alice', 'Bob', directed=False)
.add_edge('Bob', 'Charlie', directed=False)
.add_edge('Alice', 'Charlie', directed=False)
.build())
# Find influencerspr=pagerank(network)
top_influencer=max(pr, key=pr.get)
# Find power users with selectorpower_users=network.select_vertices(
v.attrs(followers=lambdaf: f>800)
)
# Analyze active subgraphanalysis= (network|filter_vertices(lambdav: v.get('followers', 0) >500)
|stats())

Road Network

fromAlgoGraphimportGraphfromAlgoGraph.algorithmsimportdijkstra, minimum_spanning_treeroads= (Graph.builder()
.add_edge('NYC', 'Boston', weight=215, directed=False)
.add_edge('NYC', 'DC', weight=225, directed=False)
.add_edge('Boston', 'DC', weight=440, directed=False)
.build())
# Shortest routes from NYCdistances=dijkstra(roads, 'NYC')
# Minimum cost networkmst=minimum_spanning_tree(roads)

Design Philosophy

  1. Immutability: All operations return new objects
  2. Composability: Chain operations with | pipe operator
  3. Declarative: Express what, not how (selectors vs lambdas)
  4. Lazy evaluation: Views defer computation until needed
  5. Type safety: Full type hints throughout

Related Projects

  • AlgoTree: Tree data structures and algorithms
  • NetworkX: Comprehensive Python graph library (mutable)
  • graph-tool: High-performance graph analysis

License

MIT License - see LICENSE file for details.

About

Immutable graph data structures and algorithms library with interactive shell

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages