pythonnetworkx

Why do I get a NetworkXError "Node has no position" when trying to draw a graph?


I'm trying to create a simulation (powered by python) to analyze density and curvature of Hyperbolic Lattices and also their realtions with Anti-de Sitter (AdS) Spaces and AdS/CFT Correspondence for a research. I'm using matplotlib, numpy, networkx, random and collections. While everything seems fine when I try to run the code, I get an error about the values. I tried to change the node_size, edge_color and with_labels values, but the error still didn't vanish.

Here is the code:

import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
import random
from collections import deque

# Parameters for hyperbolic lattice
p = 7       # Sides per polygon (heptagon)
q = 3       # Polygons per vertex
depth = 4   # Depth of recursion

# Initialize graph for lattice
G = nx.Graph()

# Define recursive function to create the lattice
def hyperbolic_polygon_vertices(sides, center, radius):
    angles = np.linspace(0, 2 * np.pi, sides, endpoint=False)
    vertices = []
    for angle in angles:
        x = center[0] + radius * np.cos(angle)
        y = center[1] + radius * np.sin(angle)
        vertices.append((x, y))
    return vertices

def add_hyperbolic_lattice(G, center, radius, depth, max_depth):
    if depth >= max_depth:
        return
    
    # Add current center node
    G.add_node(center, pos=(radius * np.cos(center[0]), radius * np.sin(center[1])))
    
    # Generate neighboring nodes
    angle_offset = 2 * np.pi / q
    for i in range(q):
        angle = i * angle_offset
        new_radius = radius * 0.8  # Adjust radius to create depth effect
        new_center = (center[0] + new_radius * np.cos(angle), 
                      center[1] + new_radius * np.sin(angle))
        
        # Add edge to graph and recursive call
        G.add_edge(center, new_center)
        add_hyperbolic_lattice(G, new_center, new_radius, depth + 1, max_depth)

# Initialize lattice with central node
initial_center = (0, 0)
initial_radius = 0.3
add_hyperbolic_lattice(G, initial_center, initial_radius, 0, depth)

# Plotting the hyperbolic lattice
pos = nx.get_node_attributes(G, 'pos')
nx.draw(G, pos, node_size=10, edge_color="blue", with_labels=False)
plt.title("Hyperbolic Lattice (Poincaré Disk)")
plt.show()

# Function to calculate density by distance
def calculate_density_by_distance(G, center, max_distance):
    distances = nx.single_source_shortest_path_length(G, center)
    density_by_distance = {}

    for distance in range(1, max_distance + 1):
        nodes_in_ring = [node for node, dist in distances.items() if dist == distance]
        if nodes_in_ring:
            area = np.pi * ((distance + 1)**2 - distance**2)  # Approximate area for each ring
            density_by_distance[distance] = len(nodes_in_ring) / area

    return density_by_distance

# Calculate and plot density vs. distance
center_node = initial_center
density_data = calculate_density_by_distance(G, center_node, max_distance=5)

plt.plot(density_data.keys(), density_data.values())
plt.xlabel("Distance from Center")
plt.ylabel("Density")
plt.title("Density vs Distance in Hyperbolic Lattice")
plt.show()

# Analyzing boundary connectivity (AdS/CFT analogy)
distances = nx.single_source_shortest_path_length(G, center_node)
max_distance = max(distances.values())
boundary_nodes = [node for node, dist in distances.items() if dist == max_distance]
boundary_connectivity = np.mean([G.degree(node) for node in boundary_nodes])

print("Average connectivity at boundary (AdS/CFT analogy):", boundary_connectivity)

# Simulating network flow to boundary (black hole thermodynamics analogy)
boundary_node = random.choice(boundary_nodes)
try:
    flow_value, _ = nx.maximum_flow(G, center_node, boundary_node)
    print("Simulated flow value (energy transfer to boundary):", flow_value)
except:
    print("Error calculating flow, nodes may not be fully connected in hyperbolic space.")

And here is the error:

Traceback (most recent call last):
  File "C:\Users\zorty\Masaüstü\.venv\Lib\site-packages\networkx\drawing\nx_pylab.py", line 445, in draw_networkx_nodes
    xy = np.asarray([pos[v] for v in nodelist])
                     ~~~^^^
KeyError: (np.float64(0.70848), np.float64(0.0))

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "c:\Users\zorty\Masaüstü\Project.py", line 51, in <module>
    nx.draw(G, pos, node_size=10, edge_color="blue", with_labels=False)
  File "C:\Users\zorty\Masaüstü\.venv\Lib\site-packages\networkx\drawing\nx_pylab.py", line 126, in draw
    draw_networkx(G, pos=pos, ax=ax, **kwds)
  File "C:\Users\zorty\Masaüstü\.venv\Lib\site-packages\networkx\drawing\nx_pylab.py", line 314, in draw_networkx
    draw_networkx_nodes(G, pos, **node_kwds)
  File "C:\Users\zorty\Masaüstü\.venv\Lib\site-packages\networkx\drawing\nx_pylab.py", line 447, in draw_networkx_nodes
    raise nx.NetworkXError(f"Node {err} has no position.") from err
networkx.exception.NetworkXError: Node (np.float64(0.70848), np.float64(0.0)) has no position.

Solution

  • Your current code does not assign positions to the nodes at depth == max_depth, resulting in nodes without defined positions. Switching the first two statements in add_hyperbolic_lattice resolves the issue.

    def add_hyperbolic_lattice(G, center, radius, depth, max_depth):   
        # Add current center node
        G.add_node(center, pos=(radius * np.cos(center[0]), radius * np.sin(center[1])))
    
        if depth >= max_depth:
            return
        
        ...