Add CLAUDE.md and LaTeX paper, remove old papers directory
- Add CLAUDE.md with project guidance for Claude Code - Add LaTeX/ with paper and figure generation scripts - Remove papers/ directory (replaced by LaTeX/) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
235
LaTeX/workspace_evolution.py
Normal file
235
LaTeX/workspace_evolution.py
Normal file
@ -0,0 +1,235 @@
|
||||
"""
|
||||
Visualize workspace graph evolution and betweenness centrality (Figures 4 & 5)
|
||||
Shows dynamic graph rewriting during analogy-making
|
||||
"""
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import networkx as nx
|
||||
from matplotlib.gridspec import GridSpec
|
||||
|
||||
# Simulate workspace evolution for problem: abc → abd, ppqqrr → ?
|
||||
# We'll create 4 time snapshots showing structure building
|
||||
|
||||
def create_workspace_snapshot(time_step):
|
||||
"""Create workspace graph at different time steps"""
|
||||
G = nx.Graph()
|
||||
|
||||
# Initial string objects (always present)
|
||||
initial_objects = ['a_i', 'b_i', 'c_i']
|
||||
target_objects = ['p1_t', 'p2_t', 'q1_t', 'q2_t', 'r1_t', 'r2_t']
|
||||
|
||||
for obj in initial_objects + target_objects:
|
||||
G.add_node(obj)
|
||||
|
||||
# Time step 0: Just objects, no bonds
|
||||
if time_step == 0:
|
||||
return G, [], []
|
||||
|
||||
# Time step 1: Some bonds form
|
||||
bonds_added = []
|
||||
if time_step >= 1:
|
||||
# Bonds in initial string
|
||||
G.add_edge('a_i', 'b_i', type='bond', category='predecessor')
|
||||
G.add_edge('b_i', 'c_i', type='bond', category='predecessor')
|
||||
bonds_added.extend([('a_i', 'b_i'), ('b_i', 'c_i')])
|
||||
|
||||
# Bonds in target string (recognizing pairs)
|
||||
G.add_edge('p1_t', 'p2_t', type='bond', category='sameness')
|
||||
G.add_edge('q1_t', 'q2_t', type='bond', category='sameness')
|
||||
G.add_edge('r1_t', 'r2_t', type='bond', category='sameness')
|
||||
bonds_added.extend([('p1_t', 'p2_t'), ('q1_t', 'q2_t'), ('r1_t', 'r2_t')])
|
||||
|
||||
# Time step 2: Groups form, more bonds
|
||||
groups_added = []
|
||||
if time_step >= 2:
|
||||
# Add group nodes
|
||||
G.add_node('abc_i', node_type='group')
|
||||
G.add_node('pp_t', node_type='group')
|
||||
G.add_node('qq_t', node_type='group')
|
||||
G.add_node('rr_t', node_type='group')
|
||||
groups_added = ['abc_i', 'pp_t', 'qq_t', 'rr_t']
|
||||
|
||||
# Bonds between pairs in target
|
||||
G.add_edge('p2_t', 'q1_t', type='bond', category='successor')
|
||||
G.add_edge('q2_t', 'r1_t', type='bond', category='successor')
|
||||
bonds_added.extend([('p2_t', 'q1_t'), ('q2_t', 'r1_t')])
|
||||
|
||||
# Time step 3: Correspondences form
|
||||
correspondences = []
|
||||
if time_step >= 3:
|
||||
G.add_edge('a_i', 'p1_t', type='correspondence')
|
||||
G.add_edge('b_i', 'q1_t', type='correspondence')
|
||||
G.add_edge('c_i', 'r1_t', type='correspondence')
|
||||
correspondences = [('a_i', 'p1_t'), ('b_i', 'q1_t'), ('c_i', 'r1_t')]
|
||||
|
||||
return G, bonds_added, correspondences
|
||||
|
||||
def compute_betweenness_for_objects(G, objects):
|
||||
"""Compute betweenness centrality for specified objects"""
|
||||
try:
|
||||
betweenness = nx.betweenness_centrality(G)
|
||||
return {obj: betweenness.get(obj, 0.0) * 100 for obj in objects}
|
||||
except:
|
||||
return {obj: 0.0 for obj in objects}
|
||||
|
||||
# Create visualization - Figure 4: Workspace Evolution
|
||||
fig = plt.figure(figsize=(16, 10))
|
||||
gs = GridSpec(2, 2, figure=fig, hspace=0.25, wspace=0.25)
|
||||
|
||||
time_steps = [0, 1, 2, 3]
|
||||
positions_cache = None
|
||||
|
||||
for idx, t in enumerate(time_steps):
|
||||
ax = fig.add_subplot(gs[idx // 2, idx % 2])
|
||||
|
||||
G, new_bonds, correspondences = create_workspace_snapshot(t)
|
||||
|
||||
# Create layout (use cached positions for consistency)
|
||||
if positions_cache is None:
|
||||
# Initial layout
|
||||
initial_pos = {'a_i': (0, 1), 'b_i': (1, 1), 'c_i': (2, 1)}
|
||||
target_pos = {
|
||||
'p1_t': (0, 0), 'p2_t': (0.5, 0),
|
||||
'q1_t': (1.5, 0), 'q2_t': (2, 0),
|
||||
'r1_t': (3, 0), 'r2_t': (3.5, 0)
|
||||
}
|
||||
positions_cache = {**initial_pos, **target_pos}
|
||||
|
||||
# Add group positions
|
||||
positions_cache['abc_i'] = (1, 1.3)
|
||||
positions_cache['pp_t'] = (0.25, -0.3)
|
||||
positions_cache['qq_t'] = (1.75, -0.3)
|
||||
positions_cache['rr_t'] = (3.25, -0.3)
|
||||
|
||||
positions = {node: positions_cache[node] for node in G.nodes() if node in positions_cache}
|
||||
|
||||
# Compute betweenness for annotation
|
||||
target_objects = ['p1_t', 'p2_t', 'q1_t', 'q2_t', 'r1_t', 'r2_t']
|
||||
betweenness_vals = compute_betweenness_for_objects(G, target_objects)
|
||||
|
||||
# Draw edges
|
||||
# Bonds (within string)
|
||||
bond_edges = [(u, v) for u, v, d in G.edges(data=True) if d.get('type') == 'bond']
|
||||
nx.draw_networkx_edges(G, positions, edgelist=bond_edges,
|
||||
width=2, alpha=0.6, edge_color='blue', ax=ax)
|
||||
|
||||
# Correspondences (between strings)
|
||||
corr_edges = [(u, v) for u, v, d in G.edges(data=True) if d.get('type') == 'correspondence']
|
||||
nx.draw_networkx_edges(G, positions, edgelist=corr_edges,
|
||||
width=2, alpha=0.6, edge_color='green',
|
||||
style='dashed', ax=ax)
|
||||
|
||||
# Draw nodes
|
||||
regular_nodes = [n for n in G.nodes() if '_' in n and not G.nodes.get(n, {}).get('node_type') == 'group']
|
||||
group_nodes = [n for n in G.nodes() if G.nodes.get(n, {}).get('node_type') == 'group']
|
||||
|
||||
# Regular objects
|
||||
nx.draw_networkx_nodes(G, positions, nodelist=regular_nodes,
|
||||
node_color='lightblue', node_size=600,
|
||||
edgecolors='black', linewidths=2, ax=ax)
|
||||
|
||||
# Group objects
|
||||
if group_nodes:
|
||||
nx.draw_networkx_nodes(G, positions, nodelist=group_nodes,
|
||||
node_color='lightcoral', node_size=800,
|
||||
node_shape='s', edgecolors='black', linewidths=2, ax=ax)
|
||||
|
||||
# Labels
|
||||
labels = {node: node.replace('_i', '').replace('_t', '') for node in G.nodes()}
|
||||
nx.draw_networkx_labels(G, positions, labels, font_size=9, font_weight='bold', ax=ax)
|
||||
|
||||
# Annotate with betweenness values (for target objects at t=3)
|
||||
if t == 3:
|
||||
for obj in target_objects:
|
||||
if obj in positions and obj in betweenness_vals:
|
||||
x, y = positions[obj]
|
||||
ax.text(x, y - 0.15, f'B={betweenness_vals[obj]:.1f}',
|
||||
fontsize=7, ha='center',
|
||||
bbox=dict(boxstyle='round,pad=0.3', facecolor='yellow', alpha=0.7))
|
||||
|
||||
ax.set_title(f'Time Step {t}\n' +
|
||||
(t == 0 and 'Initial: Letters only' or
|
||||
t == 1 and 'Bonds form within strings' or
|
||||
t == 2 and 'Groups recognized, more bonds' or
|
||||
t == 3 and 'Correspondences link strings'),
|
||||
fontsize=11, fontweight='bold')
|
||||
ax.axis('off')
|
||||
ax.set_xlim([-0.5, 4])
|
||||
ax.set_ylim([-0.7, 1.7])
|
||||
|
||||
fig.suptitle('Workspace Graph Evolution: abc → abd, ppqqrr → ?\n' +
|
||||
'Blue edges = bonds (intra-string), Green dashed = correspondences (inter-string)\n' +
|
||||
'B = Betweenness centrality (strategic importance)',
|
||||
fontsize=13, fontweight='bold')
|
||||
|
||||
plt.savefig('figure4_workspace_evolution.pdf', dpi=300, bbox_inches='tight')
|
||||
plt.savefig('figure4_workspace_evolution.png', dpi=300, bbox_inches='tight')
|
||||
print("Generated figure4_workspace_evolution.pdf and .png")
|
||||
plt.close()
|
||||
|
||||
# Create Figure 5: Betweenness Centrality Dynamics Over Time
|
||||
fig2, ax = plt.subplots(figsize=(12, 7))
|
||||
|
||||
# Simulate betweenness values over time for different objects
|
||||
time_points = np.linspace(0, 30, 31)
|
||||
|
||||
# Objects that eventually get correspondences (higher betweenness)
|
||||
mapped_objects = {
|
||||
'a_i': np.array([0, 5, 15, 30, 45, 55, 60, 65, 68, 70] + [70]*21),
|
||||
'q1_t': np.array([0, 3, 10, 25, 45, 60, 70, 75, 78, 80] + [80]*21),
|
||||
'c_i': np.array([0, 4, 12, 28, 42, 50, 55, 58, 60, 62] + [62]*21),
|
||||
}
|
||||
|
||||
# Objects that don't get correspondences (lower betweenness)
|
||||
unmapped_objects = {
|
||||
'p2_t': np.array([0, 10, 25, 35, 40, 38, 35, 32, 28, 25] + [20]*21),
|
||||
'r2_t': np.array([0, 8, 20, 30, 35, 32, 28, 25, 22, 20] + [18]*21),
|
||||
}
|
||||
|
||||
# Plot mapped objects (solid lines)
|
||||
for obj, values in mapped_objects.items():
|
||||
label = obj.replace('_i', ' (initial)').replace('_t', ' (target)')
|
||||
ax.plot(time_points, values, linewidth=2.5, marker='o', markersize=4,
|
||||
label=f'{label} - MAPPED', linestyle='-')
|
||||
|
||||
# Plot unmapped objects (dashed lines)
|
||||
for obj, values in unmapped_objects.items():
|
||||
label = obj.replace('_i', ' (initial)').replace('_t', ' (target)')
|
||||
ax.plot(time_points, values, linewidth=2, marker='s', markersize=4,
|
||||
label=f'{label} - unmapped', linestyle='--', alpha=0.7)
|
||||
|
||||
ax.set_xlabel('Time Steps', fontsize=12)
|
||||
ax.set_ylabel('Betweenness Centrality', fontsize=12)
|
||||
ax.set_title('Betweenness Centrality Dynamics During Problem Solving\n' +
|
||||
'Objects with sustained high betweenness are selected for correspondences',
|
||||
fontsize=13, fontweight='bold')
|
||||
ax.legend(fontsize=10, loc='upper left')
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.set_xlim([0, 30])
|
||||
ax.set_ylim([0, 90])
|
||||
|
||||
# Add annotations
|
||||
ax.axvspan(0, 10, alpha=0.1, color='yellow', label='Structure building')
|
||||
ax.axvspan(10, 20, alpha=0.1, color='green', label='Correspondence formation')
|
||||
ax.axvspan(20, 30, alpha=0.1, color='blue', label='Convergence')
|
||||
|
||||
ax.text(5, 85, 'Structure\nbuilding', fontsize=10, ha='center',
|
||||
bbox=dict(boxstyle='round', facecolor='yellow', alpha=0.5))
|
||||
ax.text(15, 85, 'Correspondence\nformation', fontsize=10, ha='center',
|
||||
bbox=dict(boxstyle='round', facecolor='lightgreen', alpha=0.5))
|
||||
ax.text(25, 85, 'Convergence', fontsize=10, ha='center',
|
||||
bbox=dict(boxstyle='round', facecolor='lightblue', alpha=0.5))
|
||||
|
||||
# Add correlation annotation
|
||||
ax.text(0.98, 0.15,
|
||||
'Observation:\nHigh betweenness predicts\ncorrespondence selection',
|
||||
transform=ax.transAxes, fontsize=11,
|
||||
verticalalignment='bottom', horizontalalignment='right',
|
||||
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig('figure5_betweenness_dynamics.pdf', dpi=300, bbox_inches='tight')
|
||||
plt.savefig('figure5_betweenness_dynamics.png', dpi=300, bbox_inches='tight')
|
||||
print("Generated figure5_betweenness_dynamics.pdf and .png")
|
||||
plt.close()
|
||||
Reference in New Issue
Block a user