Daniel Ari Friedman 7ca29024d8 Updates
2025-02-12 13:23:51 -08:00

7.5 KiB

title type status created complexity processing_priority tags semantic_relations
Graph Theory concept stable 2024-03-15 intermediate 1
mathematics
discrete_mathematics
networks
combinatorics
type links
foundation_for
probabilistic_graphical_models
network_science
markov_random_fields
type links
implements
discrete_mathematics
combinatorics
type links
relates
optimization_theory
information_theory
complexity_theory

Graph Theory

Overview

Graph Theory is a branch of mathematics that studies the relationships between pairs of objects. It provides the mathematical foundation for analyzing networks, connections, and structural properties in various domains, from social networks to neural architectures.

Mathematical Foundation

Basic Definitions

Graph Structure

G = (V,E)

where:

  • V is vertex set
  • E \subseteq V \times V is edge set

Graph Properties

\begin{align*}
\text{degree}(v) &= |\{u \in V : (v,u) \in E\}| \\
\text{path}(u,v) &= (v_1,\ldots,v_k), v_1=u, v_k=v \\
\text{distance}(u,v) &= \min\{k : \text{path}(u,v) \text{ has length } k\}
\end{align*}

Implementation

Graph Data Structure

class Graph:
    def __init__(self,
                 vertices: Set[Any],
                 edges: Set[Tuple[Any, Any]],
                 directed: bool = False,
                 weighted: bool = False):
        """Initialize graph.
        
        Args:
            vertices: Set of vertices
            edges: Set of edges
            directed: Whether graph is directed
            weighted: Whether graph is weighted
        """
        self.vertices = vertices
        self.edges = edges
        self.directed = directed
        self.weighted = weighted
        
        # Initialize adjacency structure
        self.adjacency = self._build_adjacency()
        
        if weighted:
            self.weights = {}
    
    def _build_adjacency(self) -> Dict[Any, Set[Any]]:
        """Build adjacency structure.
        
        Returns:
            adjacency: Adjacency dictionary
        """
        adj = {v: set() for v in self.vertices}
        
        for u, v in self.edges:
            adj[u].add(v)
            if not self.directed:
                adj[v].add(u)
        
        return adj
    
    def add_edge(self,
                u: Any,
                v: Any,
                weight: Optional[float] = None):
        """Add edge to graph.
        
        Args:
            u: First vertex
            v: Second vertex
            weight: Edge weight
        """
        self.edges.add((u, v))
        self.adjacency[u].add(v)
        
        if not self.directed:
            self.adjacency[v].add(u)
        
        if self.weighted and weight is not None:
            self.weights[(u, v)] = weight
            if not self.directed:
                self.weights[(v, u)] = weight
    
    def remove_edge(self,
                   u: Any,
                   v: Any):
        """Remove edge from graph.
        
        Args:
            u: First vertex
            v: Second vertex
        """
        self.edges.remove((u, v))
        self.adjacency[u].remove(v)
        
        if not self.directed:
            self.adjacency[v].remove(u)
        
        if self.weighted:
            del self.weights[(u, v)]
            if not self.directed:
                del self.weights[(v, u)]

Graph Algorithms

class GraphAlgorithms:
    def __init__(self,
                 graph: Graph):
        """Initialize graph algorithms.
        
        Args:
            graph: Input graph
        """
        self.graph = graph
    
    def shortest_path(self,
                     source: Any,
                     target: Any) -> Tuple[List[Any], float]:
        """Find shortest path using Dijkstra's algorithm.
        
        Args:
            source: Source vertex
            target: Target vertex
            
        Returns:
            path: Shortest path
            distance: Path length
        """
        # Initialize distances
        dist = {v: float('inf') for v in self.graph.vertices}
        dist[source] = 0
        
        # Initialize predecessors
        pred = {v: None for v in self.graph.vertices}
        
        # Priority queue
        pq = [(0, source)]
        visited = set()
        
        while pq:
            d, u = heapq.heappop(pq)
            
            if u == target:
                break
                
            if u in visited:
                continue
                
            visited.add(u)
            
            # Update neighbors
            for v in self.graph.adjacency[u]:
                weight = self.graph.weights.get((u, v), 1)
                alt = d + weight
                
                if alt < dist[v]:
                    dist[v] = alt
                    pred[v] = u
                    heapq.heappush(pq, (alt, v))
        
        # Reconstruct path
        path = []
        current = target
        
        while current is not None:
            path.append(current)
            current = pred[current]
        
        return path[::-1], dist[target]
    
    def minimum_spanning_tree(self) -> Set[Tuple[Any, Any]]:
        """Find minimum spanning tree using Kruskal's algorithm.
        
        Returns:
            mst: Minimum spanning tree edges
        """
        # Initialize disjoint sets
        parent = {v: v for v in self.graph.vertices}
        rank = {v: 0 for v in self.graph.vertices}
        
        def find(x):
            if parent[x] != x:
                parent[x] = find(parent[x])
            return parent[x]
        
        def union(x, y):
            px, py = find(x), find(y)
            if rank[px] < rank[py]:
                parent[px] = py
            elif rank[px] > rank[py]:
                parent[py] = px
            else:
                parent[py] = px
                rank[px] += 1
        
        # Sort edges by weight
        edges = sorted(
            self.graph.edges,
            key=lambda e: self.graph.weights.get(e, 1)
        )
        
        # Build MST
        mst = set()
        for u, v in edges:
            if find(u) != find(v):
                union(u, v)
                mst.add((u, v))
        
        return mst

Applications

Network Analysis

Centrality Measures

  • Degree centrality
  • Betweenness centrality
  • Eigenvector centrality
  • PageRank

Community Detection

  • Modularity optimization
  • Spectral clustering
  • Label propagation

Path Finding

Shortest Paths

  • Dijkstra's algorithm
  • Bellman-Ford algorithm
  • Floyd-Warshall algorithm

Network Flows

  • Maximum flow
  • Minimum cut
  • Bipartite matching

Best Practices

Implementation

  1. Choose appropriate representation
  2. Optimize for operations
  3. Handle special cases
  4. Consider scalability

Algorithm Design

  1. Analyze complexity
  2. Handle edge cases
  3. Optimize memory
  4. Consider parallelization

Validation

  1. Test connectivity
  2. Verify properties
  3. Check invariants
  4. Benchmark performance

Common Issues

Technical Challenges

  1. Large graph processing
  2. Dynamic updates
  3. Memory constraints
  4. Algorithm complexity

Solutions

  1. Sparse representations
  2. Incremental updates
  3. Distributed processing
  4. Approximation algorithms