Algoritmo Laplaciano de un Grafo
Introducción
El Laplaciano de un grafo es una matriz que captura la conectividad estructural de una red. Se utiliza en áreas como clustering espectral, segmentación de imágenes, optimización de redes y machine learning sobre grafos. En este artículo explicaremos su definición matemática, propiedades clave y cómo calcularlo de forma eficiente en Python.
Definiciones fundamentales
- Matriz de adyacencia (A):
A_{ij}=1si existe una arista entre los vértices i y j, 0 en caso contrario. - Matriz de grado (D): matriz diagonal donde
D_{ii}=\sum_j A_{ij}, es decir, el grado del vértice i. - Laplaciano no normalizado (L):
L = D - A. - Laplaciano normalizado (L_{sym}):
L_{sym}= I - D^{-1/2} A D^{-1/2}. - Laplaciano de paseo aleatorio (L_{rw}):
L_{rw}= I - D^{-1} A.
Propiedades clave
- Es simétrica y semidefinida positiva (para grafos no dirigidos).
- El menor valor propio es siempre
0. La multiplicidad del 0 corresponde al número de componentes conexos. - Los vectores propios asociados a los valores propios más pequeños revelan la estructura de comunidad del grafo.
Cálculo del Laplaciano en Python
Utilizaremos NetworkX para generar grafos y NumPy/SciPy para operar con matrices densas o esparcidas.
Ejemplo 1: Grafo pequeño (matriz densa)
import networkx as nx
import numpy as np
# Crear un grafo no dirigido simple
G = nx.Graph()
G.add_edges_from([(0,1), (1,2), (2,3), (3,0), (0,2)])
# Matriz de adyacencia y de grado
A = nx.adjacency_matrix(G).todense()
D = np.diag([d for n,d in G.degree()])
# Laplaciano no normalizado
L = D - A
print('Laplaciano L:\n', L)
Ejemplo 2: Grafo grande (matriz esparcida) y Laplaciano normalizado
import networkx as nx
import scipy.sparse as sp
# Generar un grafo aleatorio de Erdos‑Renyi con 10,000 nodos y p=0.001
n = 10000
p = 0.001
G = nx.erdos_renyi_graph(n, p, seed=42)
# Matriz de adyacencia esparcida
A = nx.adjacency_matrix(G) # formato CSR
# Grados como vector
deg = A.sum(axis=1).A1
D_inv_sqrt = sp.diags(1.0/np.sqrt(deg))
# Laplaciano normalizado L_sym = I - D^{-1/2} A D^{-1/2}
I = sp.identity(n, format='csr')
L_sym = I - D_inv_sqrt @ A @ D_inv_sqrt
print('Laplaciano normalizado (esparcido) creado. Shape:', L_sym.shape)
Comparativa de variantes del Laplaciano
Laplaciano no normalizado (L)
- Fácil de interpretar.
- Requiere la matriz de grado completa.
- Escala con el grado máximo del grafo.
- Uso típico en teoría espectral básica.
Laplaciano normalizado (Lsym y Lrw)
- Elimina la dependencia del tamaño del grado.
- Más estable numéricamente para grafos heterogéneos.
- Lsym es simétrico → eigen‑descomposición directa.
- Lrw se interpreta como operador de paseo aleatorio.
Aplicaciones típicas
- Clustering espectral: usar los k menores vectores propios de Lsym como embeddings y aplicar k‑means.
- Detección de comunidades: el valor propio algebraico (Fiedler) indica cuán “cortable” es el grafo.
- Reducción de dimensionalidad en grafos de alta dimensión.
- Simulación de procesos de difusión (p. ej., propagación de información).
Rendimiento y escalabilidad
El cálculo directo de eigen‑valores con matrices densas tiene complejidad O(n³), lo cual es inviable para grafos con millones de nodos. Las estrategias recomendadas son:
- Utilizar matrices esparcidas (SciPy
csr_matrix). - Aplicar algoritmos iterativos como
scipy.sparse.linalg.eigsholobpcgque solo requieren multiplicaciones matriciales. - Emplear GPU con cuGraph para grafos extremadamente grandes.
- Dividir el grafo en sub‑componentes y procesarlos en paralelo (p. ej., con Dask o Ray).
Resolución de problemas comunes (troubleshooting)
- Errores de división por cero al normalizar grados cero. Solución: eliminar nodos aislados o añadir un pequeño epsilon (
1e-10) al denominador. - Memoria insuficiente al convertir una matriz esparcida a densa. Solución: mantener siempre el formato
csry usar funciones esparcidas para eigen‑descomposición. - Convergencia lenta de eigsh. Solución: proporcionar un buen vector de inicio (
v0) o usarwhich='SM'para los menores valores propios.
Buenas prácticas y recomendaciones
- Siempre validar que el grafo sea no dirigido antes de usar el Laplaciano simétrico.
- Preferir el Laplaciano normalizado cuando los grados varían mucho.
- Documentar versiones de librerías (
networkx>=3.0,numpy>=1.26,scipy>=1.12) para reproducibilidad. - Usar tipos de datos
float32en GPUs para reducir consumo de memoria sin perder precisión significativa.
Algoritmo Laplaciano de un Grafo: Teoría, Implementación en Python y Buenas Prácticas