Add slipnet analysis: depth vs topology correlation study

Analysis shows no significant correlation between conceptual depth
and hop distance to letter nodes (r=0.281, p=0.113). Includes
Python scripts, visualizations, and LaTeX paper.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Alex Linhares
2026-02-01 20:58:15 +00:00
parent 06a42cc746
commit 50b6fbdc27
14 changed files with 4209 additions and 0 deletions

View File

@ -0,0 +1,157 @@
"""
Compute minimum hops from each node to the nearest letter node (a-z) in the slipnet.
Like Erdos number, but starting from letter nodes.
Adds 'minPathToLetter' field to each node in the JSON.
"""
import json
import networkx as nx
def load_slipnet(filepath):
with open(filepath, 'r') as f:
return json.load(f)
def save_slipnet(data, filepath):
with open(filepath, 'w') as f:
json.dump(data, f, indent=2)
def build_graph(data):
"""Build an undirected unweighted NetworkX graph from slipnet JSON."""
G = nx.Graph() # Undirected graph - edges work both ways
# Add all nodes
for node in data['nodes']:
G.add_node(node['name'])
# Add edges (unweighted - each edge counts as 1 hop)
for link in data['links']:
G.add_edge(link['source'], link['destination'])
return G
def get_letter_nodes():
"""Return set of letter nodes (a-z)."""
return set(chr(i) for i in range(ord('a'), ord('z') + 1))
def compute_min_hops_to_letters(G, letter_nodes):
"""
Compute minimum hops from each node to the nearest letter node.
Like Erdos number but for letters.
Returns dict: node_name -> {hops, path, nearest_letter}
"""
results = {}
# For each node, find shortest path (by hop count) to any letter
for node in G.nodes():
if node in letter_nodes:
# Letters have 0 hops to themselves
results[node] = {
'hops': 0,
'path': [node],
'nearestLetter': node
}
else:
min_hops = float('inf')
min_path = None
nearest_letter = None
for letter in letter_nodes:
try:
# Find shortest path by hop count (no weight parameter)
path = nx.shortest_path(G, source=node, target=letter)
hops = len(path) - 1 # Number of edges = nodes - 1
if hops < min_hops:
min_hops = hops
min_path = path
nearest_letter = letter
except nx.NetworkXNoPath:
continue
if min_path is not None:
results[node] = {
'hops': min_hops,
'path': min_path,
'nearestLetter': nearest_letter
}
else:
results[node] = {
'hops': None,
'path': None,
'nearestLetter': None
}
return results
def main():
filepath = r'C:\Users\alexa\copycat\slipnet_analysis\slipnet.json'
# Load the slipnet
data = load_slipnet(filepath)
print(f"Loaded slipnet with {data['nodeCount']} nodes and {data['linkCount']} links")
# Build the graph
G = build_graph(data)
print(f"Built graph with {G.number_of_nodes()} nodes and {G.number_of_edges()} edges")
# Get letter nodes
letter_nodes = get_letter_nodes()
print(f"Letter nodes: {sorted(letter_nodes)}")
# Compute minimum hops to letters
path_results = compute_min_hops_to_letters(G, letter_nodes)
# Find max hops among reachable nodes
max_hops = max(r['hops'] for r in path_results.values() if r['hops'] is not None)
unreachable_hops = 2 * max_hops
print(f"Max hops among reachable nodes: {max_hops}")
print(f"Assigning unreachable nodes hops = 2 * {max_hops} = {unreachable_hops}")
# Assign unreachable nodes hops = 2 * max_hops
for node_name, result in path_results.items():
if result['hops'] is None:
result['hops'] = unreachable_hops
result['path'] = None # No path exists
result['nearestLetter'] = None
# Add results to each node in the JSON
for node in data['nodes']:
node_name = node['name']
if node_name in path_results:
result = path_results[node_name]
node['minPathToLetter'] = {
'hops': result['hops'],
'path': result['path'],
'nearestLetter': result['nearestLetter']
}
# Save the updated JSON
save_slipnet(data, filepath)
print(f"\nUpdated slipnet saved to {filepath}")
# Print summary sorted by hops
print("\n=== Summary of minimum hops to letter nodes (Erdos-style) ===")
reachable_nodes = [(n['name'], n['minPathToLetter']) for n in data['nodes']
if 'minPathToLetter' in n and n['minPathToLetter']['path'] is not None
and n['minPathToLetter']['hops'] > 0]
unreachable_nodes = [(n['name'], n['minPathToLetter']) for n in data['nodes']
if 'minPathToLetter' in n and n['minPathToLetter']['path'] is None
and n['minPathToLetter']['hops'] > 0]
# Sort by hops, then by name
reachable_nodes.sort(key=lambda x: (x[1]['hops'], x[0]))
unreachable_nodes.sort(key=lambda x: x[0])
print(f"\n{'Node':<30} {'Hops':<6} {'Nearest':<8} {'Path'}")
print("-" * 80)
for name, info in reachable_nodes:
path_str = ' -> '.join(info['path'])
print(f"{name:<30} {info['hops']:<6} {info['nearestLetter']:<8} {path_str}")
if unreachable_nodes:
print(f"\nUnreachable nodes (assigned hops = {unreachable_hops}):")
for name, info in unreachable_nodes:
print(f" {name:<30} (depth: {[n['conceptualDepth'] for n in data['nodes'] if n['name'] == name][0]})")
if __name__ == '__main__':
main()