npcpy 1.3.21__py3-none-any.whl → 1.3.23__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- npcpy/data/audio.py +58 -286
- npcpy/data/image.py +15 -15
- npcpy/data/web.py +2 -2
- npcpy/gen/audio_gen.py +172 -2
- npcpy/gen/image_gen.py +113 -62
- npcpy/gen/response.py +239 -0
- npcpy/llm_funcs.py +73 -71
- npcpy/memory/command_history.py +117 -69
- npcpy/memory/kg_vis.py +74 -74
- npcpy/npc_compiler.py +261 -26
- npcpy/npc_sysenv.py +4 -1
- npcpy/serve.py +393 -91
- npcpy/work/desktop.py +31 -5
- npcpy-1.3.23.dist-info/METADATA +416 -0
- {npcpy-1.3.21.dist-info → npcpy-1.3.23.dist-info}/RECORD +18 -18
- npcpy-1.3.21.dist-info/METADATA +0 -1039
- {npcpy-1.3.21.dist-info → npcpy-1.3.23.dist-info}/WHEEL +0 -0
- {npcpy-1.3.21.dist-info → npcpy-1.3.23.dist-info}/licenses/LICENSE +0 -0
- {npcpy-1.3.21.dist-info → npcpy-1.3.23.dist-info}/top_level.txt +0 -0
npcpy/memory/kg_vis.py
CHANGED
|
@@ -65,27 +65,27 @@ def visualize_knowledge_graph_final_interactive(kg, filename="knowledge_graph.ht
|
|
|
65
65
|
"""Updated to work with the new KG structure"""
|
|
66
66
|
print(f"Generating interactive graph for Gen {kg['generation']} -> {filename}")
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
# Extract facts and concepts from the new structure
|
|
69
69
|
facts = kg.get("facts", [])
|
|
70
70
|
concepts = kg.get("concepts", [])
|
|
71
71
|
fact_to_concept_links = kg.get("fact_to_concept_links", {})
|
|
72
72
|
concept_links = kg.get("concept_links", [])
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
# Create node map for easy lookup
|
|
75
75
|
node_map = {}
|
|
76
76
|
for fact in facts:
|
|
77
77
|
node_map[fact['statement']] = fact
|
|
78
78
|
for concept in concepts:
|
|
79
79
|
node_map[concept['name']] = concept
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
# Calculate positions in concentric circles
|
|
82
|
+
# Facts in inner circle, concepts in outer circle
|
|
83
83
|
fact_radius = 300
|
|
84
84
|
concept_radius = 600
|
|
85
85
|
|
|
86
86
|
node_positions = {}
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
# Position facts
|
|
89
89
|
if facts:
|
|
90
90
|
for i, fact in enumerate(facts):
|
|
91
91
|
angle = (2 * math.pi * i) / len(facts)
|
|
@@ -95,7 +95,7 @@ def visualize_knowledge_graph_final_interactive(kg, filename="knowledge_graph.ht
|
|
|
95
95
|
'y': fact_radius * math.sin(angle)
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
# Position concepts
|
|
99
99
|
if concepts:
|
|
100
100
|
for i, concept in enumerate(concepts):
|
|
101
101
|
angle = (2 * math.pi * i) / len(concepts)
|
|
@@ -105,10 +105,10 @@ def visualize_knowledge_graph_final_interactive(kg, filename="knowledge_graph.ht
|
|
|
105
105
|
'y': concept_radius * math.sin(angle)
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
# Create the network
|
|
109
|
+
net = Network(height="100vh", width="100%", bgcolor="#222222", font_color="white", directed=True)
|
|
108
110
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
111
|
+
# Add fact nodes
|
|
112
112
|
for fact in facts:
|
|
113
113
|
node_id = fact['statement']
|
|
114
114
|
pos = node_positions.get(node_id, {'x': 0, 'y': 0})
|
|
@@ -119,11 +119,11 @@ def visualize_knowledge_graph_final_interactive(kg, filename="knowledge_graph.ht
|
|
|
119
119
|
title=title_text,
|
|
120
120
|
x=pos['x'],
|
|
121
121
|
y=pos['y'],
|
|
122
|
-
color='
|
|
122
|
+
color='#ff6961', # Red for facts
|
|
123
123
|
physics=False
|
|
124
124
|
)
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
# Add concept nodes
|
|
127
127
|
for concept in concepts:
|
|
128
128
|
node_id = concept['name']
|
|
129
129
|
pos = node_positions.get(node_id, {'x': 0, 'y': 0})
|
|
@@ -134,17 +134,17 @@ def visualize_knowledge_graph_final_interactive(kg, filename="knowledge_graph.ht
|
|
|
134
134
|
title=title_text,
|
|
135
135
|
x=pos['x'],
|
|
136
136
|
y=pos['y'],
|
|
137
|
-
color='
|
|
137
|
+
color='#ffb480', # Orange for concepts
|
|
138
138
|
physics=False
|
|
139
139
|
)
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
# Add fact-to-concept edges
|
|
142
142
|
for fact_statement, concept_names in fact_to_concept_links.items():
|
|
143
143
|
for concept_name in concept_names:
|
|
144
144
|
if fact_statement in node_map and concept_name in node_map:
|
|
145
|
-
net.add_edge(fact_statement, concept_name, color="
|
|
146
|
-
|
|
145
|
+
net.add_edge(fact_statement, concept_name, color="#8ecae6", width=1)
|
|
147
146
|
|
|
147
|
+
# Add concept-to-con
|
|
148
148
|
def visualize_growth(k_graphs, filename="growth_chart.png"):
|
|
149
149
|
"""
|
|
150
150
|
Plots Facts and Concepts as separate lines instead of a stacked area.
|
|
@@ -158,15 +158,15 @@ def visualize_growth(k_graphs, filename="growth_chart.png"):
|
|
|
158
158
|
plt.figure(figsize=(12, 8))
|
|
159
159
|
|
|
160
160
|
|
|
161
|
-
plt.plot(gens, facts_counts, label='Facts', color='
|
|
162
|
-
plt.plot(gens, concepts_counts, label='Concepts', color='
|
|
163
|
-
plt.plot(gens, total_nodes, label='Total Nodes', color='
|
|
161
|
+
plt.plot(gens, facts_counts, label='Facts', color='#ff6961', marker='o', linestyle='-', linewidth=2)
|
|
162
|
+
plt.plot(gens, concepts_counts, label='Concepts', color='#ffb480', marker='o', linestyle='-', linewidth=2)
|
|
163
|
+
plt.plot(gens, total_nodes, label='Total Nodes', color='#8ecae6', marker='x', linestyle='--', alpha=0.7)
|
|
164
164
|
|
|
165
165
|
plt.xlabel("Generation", fontsize=14)
|
|
166
166
|
plt.ylabel("Number of Nodes", fontsize=14)
|
|
167
167
|
plt.legend(loc='upper left', fontsize=20, frameon=False)
|
|
168
168
|
plt.xticks(gens)
|
|
169
|
-
plt.ylim(bottom=0)
|
|
169
|
+
plt.ylim(bottom=0) # Ensure the y-axis starts at 0
|
|
170
170
|
|
|
171
171
|
plt.savefig(filename, dpi=300, bbox_inches='tight')
|
|
172
172
|
plt.close()
|
|
@@ -190,8 +190,8 @@ def visualize_fact_concept_ratio(kg_pairs, filename="fact_concept_ratio.png"):
|
|
|
190
190
|
x = np.arange(len(labels))
|
|
191
191
|
width = 0.35
|
|
192
192
|
fig, ax = plt.subplots(figsize=(12, 8))
|
|
193
|
-
rects1 = ax.bar(x - width/2, before_ratios, width, label='Before Sleep', color='
|
|
194
|
-
rects2 = ax.bar(x + width/2, after_ratios, width, label='After Sleep', color='
|
|
193
|
+
rects1 = ax.bar(x - width/2, before_ratios, width, label='Before Sleep', color='#95a5a6')
|
|
194
|
+
rects2 = ax.bar(x + width/2, after_ratios, width, label='After Sleep', color='#2ecc71')
|
|
195
195
|
ax.set_ylabel("Fact-to-Concept Ratio",)
|
|
196
196
|
ax.set_xticks(x, labels, fontsize=12, rotation=45, ha="right")
|
|
197
197
|
ax.legend(fontsize=20, frameon=False)
|
|
@@ -208,21 +208,21 @@ def visualize_sleep_process(kg_before, kg_after, filename="sleep_process.png"):
|
|
|
208
208
|
|
|
209
209
|
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 8))
|
|
210
210
|
|
|
211
|
-
|
|
211
|
+
# Before state
|
|
212
212
|
facts_before = len(kg_before.get('facts', []))
|
|
213
213
|
concepts_before = len(kg_before.get('concepts', []))
|
|
214
214
|
ax1.pie([facts_before, concepts_before], labels=['Facts', 'Concepts'],
|
|
215
|
-
colors=['
|
|
216
|
-
|
|
217
|
-
|
|
215
|
+
colors=['#ff6961', '#ffb480'], autopct='%1.1f%%')
|
|
216
|
+
#ax1.set_title(f"Before Sleep (Gen {kg_before['generation']})")
|
|
218
217
|
|
|
218
|
+
# After state
|
|
219
219
|
facts_after = len(kg_after.get('facts', []))
|
|
220
220
|
concepts_after = len(kg_after.get('concepts', []))
|
|
221
221
|
ax2.pie([facts_after, concepts_after], labels=['Facts', 'Concepts'],
|
|
222
|
-
colors=['
|
|
223
|
-
|
|
224
|
-
|
|
222
|
+
colors=['#ff6961', '#ffb480'], autopct='%1.1f%%')
|
|
223
|
+
#ax2.set_title(f"After Sleep (Gen {kg_after['generation']})")
|
|
225
224
|
|
|
225
|
+
#plt.suptitle(f"Sleep Process: Generation {kg_before['generation']} to {kg_after['generation']}")
|
|
226
226
|
plt.savefig(filename, bbox_inches='tight', dpi=300)
|
|
227
227
|
plt.close()
|
|
228
228
|
print(f"Saved sleep process visualization to {filename}")
|
|
@@ -266,11 +266,11 @@ def visualize_key_experiences(kg, filename="key_experiences.png"):
|
|
|
266
266
|
node_colors = []
|
|
267
267
|
for node in G:
|
|
268
268
|
if node in top_facts:
|
|
269
|
-
node_colors.append('
|
|
269
|
+
node_colors.append('#ff0000') # Bright Red for key facts
|
|
270
270
|
elif G.nodes[node]['type'] == 'fact':
|
|
271
|
-
node_colors.append('
|
|
271
|
+
node_colors.append('#ff6961') # Standard Red for facts
|
|
272
272
|
else:
|
|
273
|
-
node_colors.append('
|
|
273
|
+
node_colors.append('#ffb480') # Orange for concepts
|
|
274
274
|
|
|
275
275
|
plt.figure(figsize=(24, 24))
|
|
276
276
|
pos = nx.spring_layout(G, k=1.5/math.sqrt(G.number_of_nodes()), iterations=100, seed=42)
|
|
@@ -324,10 +324,10 @@ def visualize_concept_trajectories(kg_history, n_pillars=2, n_risers=3, filename
|
|
|
324
324
|
centrality_df = pd.concat([centrality_df, s.to_frame()], axis=1)
|
|
325
325
|
centrality_df = centrality_df.transpose().sort_index()
|
|
326
326
|
|
|
327
|
-
|
|
327
|
+
# Pillars: High average centrality
|
|
328
328
|
pillars = centrality_df.mean().nlargest(n_pillars).index
|
|
329
329
|
|
|
330
|
-
|
|
330
|
+
# Rising Stars: Exclude pillars from the candidates to find distinct risers
|
|
331
331
|
riser_candidates = centrality_df.drop(columns=pillars, errors='ignore')
|
|
332
332
|
centrality_diff = riser_candidates.iloc[-1].fillna(0) - riser_candidates.iloc[0].fillna(0)
|
|
333
333
|
risers = centrality_diff.nlargest(n_risers).index
|
|
@@ -344,10 +344,10 @@ def visualize_concept_trajectories(kg_history, n_pillars=2, n_risers=3, filename
|
|
|
344
344
|
label=fill(concept_name, 20), linewidth=linewidth, alpha=alpha)
|
|
345
345
|
plt.xlabel("Generation", fontsize=14)
|
|
346
346
|
plt.ylabel("Degree Centrality", fontsize=14)
|
|
347
|
-
|
|
347
|
+
#plt.title("Notable Concept Trajectories (Pillars vs. Risers)", fontsize=16)
|
|
348
348
|
plt.xticks(gens)
|
|
349
349
|
plt.legend(title="Concepts", bbox_to_anchor=(1.05, 1), loc='upper left')
|
|
350
|
-
|
|
350
|
+
#plt.grid(True, linestyle='--', alpha=0.6)
|
|
351
351
|
plt.ylim(bottom=0)
|
|
352
352
|
plt.tight_layout()
|
|
353
353
|
plt.savefig(filename, dpi=300, bbox_inches='tight')
|
|
@@ -363,7 +363,7 @@ def visualize_associative_richness(kg_history, filename="associative_richness.pn
|
|
|
363
363
|
ari_scores.append(total_links / num_facts if num_facts > 0 else 0)
|
|
364
364
|
|
|
365
365
|
plt.figure(figsize=(12, 8))
|
|
366
|
-
plt.plot(gens, ari_scores, marker='o', linestyle='-', color='
|
|
366
|
+
plt.plot(gens, ari_scores, marker='o', linestyle='-', color='#6a0dad', linewidth=2.5, label="System's ARI")
|
|
367
367
|
plt.axhline(y=1, color='gray', linestyle='--', linewidth=2, label='1-to-1 Mapping Baseline (ARI=1.0)')
|
|
368
368
|
plt.xlabel("Generation")
|
|
369
369
|
plt.ylabel("Avg. Concepts per Fact (ARI)")
|
|
@@ -384,7 +384,7 @@ def visualize_conceptual_support(kg_history, filename="conceptual_support.png"):
|
|
|
384
384
|
csi_scores.append(total_links / num_concepts if num_concepts > 0 else 0)
|
|
385
385
|
|
|
386
386
|
plt.figure(figsize=(12, 8))
|
|
387
|
-
plt.plot(gens, csi_scores, marker='o', linestyle='-', color='
|
|
387
|
+
plt.plot(gens, csi_scores, marker='o', linestyle='-', color='#17becf', linewidth=2.5, label="System's CSI")
|
|
388
388
|
plt.xlabel("Generation")
|
|
389
389
|
plt.ylabel("Avg. Facts per Concept (CSI)")
|
|
390
390
|
plt.xticks(gens)
|
|
@@ -400,7 +400,7 @@ def visualize_specialist_concepts(kg_history, num_to_show=8, filename="specialis
|
|
|
400
400
|
"""
|
|
401
401
|
print(f"Generating Specialist Concept Trajectories chart -> {filename}")
|
|
402
402
|
centrality_df = pd.DataFrame()
|
|
403
|
-
gens = [kg['generation'] for kg in kg_history]
|
|
403
|
+
gens = [kg['generation'] for kg in kg_history] # Define gens here
|
|
404
404
|
|
|
405
405
|
for kg in kg_history:
|
|
406
406
|
G = _create_networkx_graph(kg)
|
|
@@ -424,7 +424,7 @@ def visualize_specialist_concepts(kg_history, num_to_show=8, filename="specialis
|
|
|
424
424
|
plt.xlabel("Generation")
|
|
425
425
|
plt.ylabel("Degree Centrality")
|
|
426
426
|
|
|
427
|
-
plt.xticks(gens)
|
|
427
|
+
plt.xticks(gens) # Use the 'gens' list, not a single value
|
|
428
428
|
|
|
429
429
|
plt.legend(title="Specialist Concepts", loc=0, fontsize=17, frameon=False)
|
|
430
430
|
plt.ylim(bottom=0)
|
|
@@ -460,8 +460,8 @@ def visualize_static_network(kg, top_n_concepts=25, top_n_facts=50, filename="st
|
|
|
460
460
|
plt.figure(figsize=(16, 24))
|
|
461
461
|
|
|
462
462
|
|
|
463
|
-
nx.draw_networkx_nodes(SubG, pos, nodelist=top_facts, node_color='
|
|
464
|
-
nx.draw_networkx_nodes(SubG, pos, nodelist=top_concepts, node_color='
|
|
463
|
+
nx.draw_networkx_nodes(SubG, pos, nodelist=top_facts, node_color='#ff6961', node_size=150)
|
|
464
|
+
nx.draw_networkx_nodes(SubG, pos, nodelist=top_concepts, node_color='#ffb480', node_size=1200)
|
|
465
465
|
|
|
466
466
|
|
|
467
467
|
nx.draw_networkx_edges(SubG, pos, alpha=0.25, width=0.6, edge_color='gray')
|
|
@@ -507,7 +507,7 @@ def visualize_concept_ontology_graph(kg, filename="concept_ontology.png"):
|
|
|
507
507
|
|
|
508
508
|
pos = nx.spring_layout(Concept_G, k=1.5/math.sqrt(Concept_G.number_of_nodes()), iterations=100, seed=42)
|
|
509
509
|
|
|
510
|
-
nx.draw_networkx_nodes(Concept_G, pos, node_color='
|
|
510
|
+
nx.draw_networkx_nodes(Concept_G, pos, node_color='#ffb480', node_size=node_sizes)
|
|
511
511
|
nx.draw_networkx_edges(Concept_G, pos, alpha=0.6, width=1.0, edge_color='gray')
|
|
512
512
|
nx.draw_networkx_labels(Concept_G, pos, font_size=14, font_family='serif')
|
|
513
513
|
|
|
@@ -531,13 +531,13 @@ def visualize_top_concept_centrality(kg_history, top_n=5, filename="concept_cent
|
|
|
531
531
|
|
|
532
532
|
degree_centrality = nx.degree_centrality(G)
|
|
533
533
|
|
|
534
|
-
|
|
534
|
+
# Filter for concepts only
|
|
535
535
|
concept_centrality = {node: cent for node, cent in degree_centrality.items() if G.nodes[node]['type'] == 'concept'}
|
|
536
536
|
|
|
537
537
|
for concept_name, centrality in concept_centrality.items():
|
|
538
538
|
centrality_data[concept_name][i] = centrality
|
|
539
539
|
|
|
540
|
-
|
|
540
|
+
# Find the top N concepts based on their peak centrality
|
|
541
541
|
sorted_concepts = sorted(centrality_data.keys(), key=lambda c: np.nanmax(centrality_data[c]), reverse=True)
|
|
542
542
|
top_concepts = sorted_concepts[:top_n]
|
|
543
543
|
|
|
@@ -545,7 +545,7 @@ def visualize_top_concept_centrality(kg_history, top_n=5, filename="concept_cent
|
|
|
545
545
|
gens = [kg['generation'] for kg in kg_history]
|
|
546
546
|
|
|
547
547
|
for concept_name in top_concepts:
|
|
548
|
-
|
|
548
|
+
# Interpolate NaN values for smoother plotting if a concept disappears and reappears
|
|
549
549
|
s = pd.Series(centrality_data[concept_name])
|
|
550
550
|
s_interpolated = s.interpolate(method='linear', limit_direction='forward', axis=0)
|
|
551
551
|
plt.plot(gens, s_interpolated, marker='o', linestyle='-', label=fill(concept_name, 20))
|
|
@@ -569,7 +569,7 @@ def visualize_lorenz_curve(kg_history, filename="lorenz_curve.png"):
|
|
|
569
569
|
|
|
570
570
|
fig, ax = plt.subplots(figsize=(10, 10))
|
|
571
571
|
|
|
572
|
-
|
|
572
|
+
# --- Lorenz curve for the first valid generation ---
|
|
573
573
|
first_gen_kg = next((kg for kg in kg_history if kg.get('facts')), None)
|
|
574
574
|
if first_gen_kg:
|
|
575
575
|
G_first = _create_networkx_graph(first_gen_kg)
|
|
@@ -577,24 +577,24 @@ def visualize_lorenz_curve(kg_history, filename="lorenz_curve.png"):
|
|
|
577
577
|
if degrees_first.size > 0:
|
|
578
578
|
cum_degrees_first = np.cumsum(degrees_first)
|
|
579
579
|
ax.plot(np.linspace(0, 1, len(degrees_first)), cum_degrees_first / cum_degrees_first[-1],
|
|
580
|
-
label=f"Gen {first_gen_kg['generation']} (Start)", color='
|
|
580
|
+
label=f"Gen {first_gen_kg['generation']} (Start)", color='#1f77b4', linewidth=2)
|
|
581
581
|
|
|
582
|
-
|
|
582
|
+
# --- Lorenz curve for the last generation ---
|
|
583
583
|
last_gen_kg = kg_history[-1]
|
|
584
584
|
G_last = _create_networkx_graph(last_gen_kg)
|
|
585
585
|
degrees_last = np.array(sorted([d for n, d in G_last.degree()]))
|
|
586
586
|
if degrees_last.size > 0:
|
|
587
587
|
cum_degrees_last = np.cumsum(degrees_last)
|
|
588
588
|
ax.plot(np.linspace(0, 1, len(degrees_last)), cum_degrees_last / cum_degrees_last[-1],
|
|
589
|
-
label=f"Gen {last_gen_kg['generation']} (End)", color='
|
|
589
|
+
label=f"Gen {last_gen_kg['generation']} (End)", color='#ff7f0e', linewidth=2)
|
|
590
590
|
|
|
591
|
-
|
|
591
|
+
# --- Line of perfect equality for reference ---
|
|
592
592
|
ax.plot([0, 1], [0, 1], linestyle='--', color='black', label='Perfect Equality')
|
|
593
593
|
|
|
594
594
|
ax.set_xlabel("Cumulative Share of Nodes", fontsize=14)
|
|
595
595
|
ax.set_ylabel("Cumulative Share of Connections", fontsize=14)
|
|
596
596
|
ax.legend(fontsize=12)
|
|
597
|
-
ax.set_aspect('equal', adjustable='box')
|
|
597
|
+
ax.set_aspect('equal', adjustable='box') # Ensures the plot is square
|
|
598
598
|
plt.tight_layout()
|
|
599
599
|
plt.savefig(filename, dpi=300, bbox_inches='tight')
|
|
600
600
|
plt.close()
|
|
@@ -631,11 +631,11 @@ def visualize_concept_bubble_chart(kg, filename="concept_bubble_chart.png"):
|
|
|
631
631
|
|
|
632
632
|
central_node = max(concepts, key=concepts.get)
|
|
633
633
|
fixed_nodes = [central_node]
|
|
634
|
-
pos_initial = {central_node: (0, 0)}
|
|
634
|
+
pos_initial = {central_node: (0, 0)} # Pin the most important node at (0,0)
|
|
635
635
|
|
|
636
636
|
|
|
637
637
|
pos = nx.spring_layout(Concept_G, pos=pos_initial, fixed=fixed_nodes,
|
|
638
|
-
k=1.8/math.sqrt(Concept_G.number_of_nodes()),
|
|
638
|
+
k=1.8/math.sqrt(Concept_G.number_of_nodes()), # Controls spacing
|
|
639
639
|
iterations=200, seed=42)
|
|
640
640
|
|
|
641
641
|
|
|
@@ -644,7 +644,7 @@ def visualize_concept_bubble_chart(kg, filename="concept_bubble_chart.png"):
|
|
|
644
644
|
|
|
645
645
|
node_sizes = [concepts[node] * 200 for node in Concept_G.nodes()]
|
|
646
646
|
|
|
647
|
-
nx.draw_networkx_nodes(Concept_G, pos, node_color='
|
|
647
|
+
nx.draw_networkx_nodes(Concept_G, pos, node_color='#ffb480', node_size=node_sizes, alpha=0.9)
|
|
648
648
|
|
|
649
649
|
for node, (x, y) in pos.items():
|
|
650
650
|
degree = concepts[node]
|
|
@@ -712,51 +712,51 @@ def visualize_centrality_bubble_chart(kg, node_type="concepts", filename="concep
|
|
|
712
712
|
radius += 0.20
|
|
713
713
|
nodes_in_ring = int(nodes_in_ring * 1.5)
|
|
714
714
|
|
|
715
|
-
|
|
715
|
+
# Step 4: Draw the plot
|
|
716
716
|
plt.figure(figsize=(20, 20))
|
|
717
717
|
|
|
718
718
|
if node_type == "both":
|
|
719
|
-
|
|
719
|
+
# Separate and draw by type
|
|
720
720
|
concept_nodes = [(name, data) for name, data in sorted_nodes if data['type'] == 'concept']
|
|
721
721
|
fact_nodes = [(name, data) for name, data in sorted_nodes if data['type'] == 'fact']
|
|
722
722
|
|
|
723
|
-
|
|
723
|
+
# Draw concepts
|
|
724
724
|
if concept_nodes:
|
|
725
725
|
concept_names = [item[0] for item in concept_nodes]
|
|
726
726
|
concept_sizes = [item[1]['degree'] * 200 for item in concept_nodes]
|
|
727
727
|
concept_pos = {name: pos[name] for name in concept_names if name in pos}
|
|
728
728
|
nx.draw_networkx_nodes(None, concept_pos, nodelist=concept_names,
|
|
729
|
-
node_color='
|
|
730
|
-
|
|
729
|
+
node_color='#ffb480', node_size=concept_sizes, alpha=0.9)
|
|
731
730
|
|
|
731
|
+
# Draw facts
|
|
732
732
|
if fact_nodes:
|
|
733
733
|
fact_names = [item[0] for item in fact_nodes]
|
|
734
734
|
fact_sizes = [item[1]['degree'] * 100 for item in fact_nodes]
|
|
735
735
|
fact_pos = {name: pos[name] for name in fact_names if name in pos}
|
|
736
736
|
nx.draw_networkx_nodes(None, fact_pos, nodelist=fact_names,
|
|
737
|
-
node_color='
|
|
738
|
-
|
|
737
|
+
node_color='#ff6961', node_size=fact_sizes, alpha=0.9)
|
|
739
738
|
|
|
739
|
+
# Add legend for both types
|
|
740
740
|
from matplotlib.patches import Patch
|
|
741
741
|
legend_elements = [
|
|
742
|
-
Patch(facecolor='
|
|
743
|
-
Patch(facecolor='
|
|
742
|
+
Patch(facecolor='#ffb480', label='Concepts'),
|
|
743
|
+
Patch(facecolor='#ff6961', label='Facts')
|
|
744
744
|
]
|
|
745
745
|
plt.legend(handles=legend_elements, loc='upper right', fontsize=14)
|
|
746
746
|
|
|
747
747
|
else:
|
|
748
|
-
|
|
748
|
+
# Single type visualization
|
|
749
749
|
node_names = [item[0] for item in sorted_nodes]
|
|
750
750
|
node_sizes = [item[1] * 200 for item in sorted_nodes]
|
|
751
751
|
|
|
752
|
-
|
|
753
|
-
color = '
|
|
752
|
+
# Choose color based on type
|
|
753
|
+
color = '#ffb480' if node_type == "concepts" else '#ff6961'
|
|
754
754
|
|
|
755
755
|
nx.draw_networkx_nodes(None, pos, nodelist=node_names,
|
|
756
756
|
node_color=color, node_size=node_sizes, alpha=0.9)
|
|
757
757
|
|
|
758
758
|
|
|
759
|
-
top_nodes = sorted_nodes[:min(20, len(sorted_nodes))]
|
|
759
|
+
top_nodes = sorted_nodes[:min(20, len(sorted_nodes))] # Top 20 nodes only
|
|
760
760
|
|
|
761
761
|
for item in top_nodes:
|
|
762
762
|
node_name = item[0]
|
|
@@ -765,13 +765,13 @@ def visualize_centrality_bubble_chart(kg, node_type="concepts", filename="concep
|
|
|
765
765
|
node_type_actual = item[1]['type']
|
|
766
766
|
else:
|
|
767
767
|
degree = item[1]
|
|
768
|
-
node_type_actual = node_type.rstrip('s')
|
|
768
|
+
node_type_actual = node_type.rstrip('s') # "concepts" -> "concept"
|
|
769
769
|
|
|
770
770
|
if node_name in pos:
|
|
771
771
|
x, y = pos[node_name]
|
|
772
772
|
font_size = max(8, 8 + 2 * np.log1p(degree))
|
|
773
773
|
|
|
774
|
-
|
|
774
|
+
# Adjust label length based on type
|
|
775
775
|
if node_type_actual == 'fact':
|
|
776
776
|
label = fill(node_name, 10)
|
|
777
777
|
else:
|
|
@@ -782,7 +782,7 @@ def visualize_centrality_bubble_chart(kg, node_type="concepts", filename="concep
|
|
|
782
782
|
|
|
783
783
|
plt.axis('off')
|
|
784
784
|
|
|
785
|
-
|
|
785
|
+
# Set axis limits
|
|
786
786
|
max_coord = radius * 1.1
|
|
787
787
|
plt.xlim(-max_coord, max_coord)
|
|
788
788
|
plt.ylim(-max_coord, max_coord)
|
|
@@ -798,8 +798,8 @@ def visualize_dual_richness_metrics(kg_history, filename="dual_richness_metrics.
|
|
|
798
798
|
print(f"Generating Dual Richness Metrics chart -> {filename}")
|
|
799
799
|
|
|
800
800
|
gens = [kg['generation'] for kg in kg_history]
|
|
801
|
-
ari_scores = []
|
|
802
|
-
csi_scores = []
|
|
801
|
+
ari_scores = [] # Concepts per Fact
|
|
802
|
+
csi_scores = [] # Facts per Concept
|
|
803
803
|
|
|
804
804
|
for kg in kg_history:
|
|
805
805
|
num_facts = len(kg.get('facts', []))
|
|
@@ -814,14 +814,14 @@ def visualize_dual_richness_metrics(kg_history, filename="dual_richness_metrics.
|
|
|
814
814
|
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 16), sharex=True)
|
|
815
815
|
|
|
816
816
|
|
|
817
|
-
ax1.plot(gens, ari_scores, marker='o', linestyle='-', color='
|
|
817
|
+
ax1.plot(gens, ari_scores, marker='o', linestyle='-', color='#6a0dad', linewidth=2.5, label="System's ARI")
|
|
818
818
|
ax1.axhline(y=1, color='gray', linestyle='--', linewidth=2, label='1-to-1 Mapping Baseline (ARI=1.0)')
|
|
819
819
|
ax1.set_ylabel("Avg. Concepts per Fact (ARI)", fontsize=14)
|
|
820
820
|
ax1.legend(loc='lower right')
|
|
821
821
|
ax1.set_ylim(bottom=0)
|
|
822
822
|
|
|
823
823
|
|
|
824
|
-
ax2.plot(gens, csi_scores, marker='o', linestyle='-', color='
|
|
824
|
+
ax2.plot(gens, csi_scores, marker='o', linestyle='-', color='#17becf', linewidth=2.5, label="System's CSI")
|
|
825
825
|
ax2.set_xlabel("Generation", fontsize=14)
|
|
826
826
|
ax2.set_ylabel("Avg. Facts per Concept (CSI)", fontsize=14)
|
|
827
827
|
ax2.legend(loc='lower right')
|