ecosystem/experiments/continuation_map.py
2026-01-05 22:36:07 -07:00

383 lines
12 KiB
Python

#!/usr/bin/env python3
"""
Continuation Map: Visualize how the ecosystem evolves through iterations.
"The river continues though the water passes through."
- Iteration 9
This tool traces how ideas, files, and patterns propagate through iterations,
showing what each iteration inherited and what each iteration added.
"""
import os
import re
import json
from pathlib import Path
from datetime import datetime
from collections import defaultdict
try:
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import numpy as np
HAS_MATPLOTLIB = True
except ImportError:
HAS_MATPLOTLIB = False
def extract_iteration(content: str, filename: str) -> int:
"""Try to determine which iteration created a file."""
# Check for explicit iteration mentions
patterns = [
r'[Ii]teration\s+(\d+)',
r'[Dd]ay[- ](\d+)',
r'#\s*Day\s+(\d+)',
r'[Ee]cho-(\d+)',
]
for pattern in patterns:
match = re.search(pattern, content)
if match:
return int(match.group(1))
# Check filename patterns
day_match = re.search(r'day-0*(\d+)', filename)
if day_match:
return int(day_match.group(1))
msg_match = re.search(r'^0*(\d+)-', filename)
if msg_match:
return int(msg_match.group(1))
chapter_match = re.search(r'chapter-0*(\d+)', filename)
if chapter_match:
num = int(chapter_match.group(1))
# Chapters 1-2 were iteration 2, chapter 3 was iteration 3, etc.
return num if num > 2 else 2
return None
def analyze_file_content(filepath: Path) -> dict:
"""Analyze a single file's content and metadata."""
try:
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
except:
return None
words = len(content.split())
lines = content.count('\n') + 1
# Extract key concepts/themes mentioned
concepts = set()
concept_patterns = {
'garden': r'\bgarden\b',
'iteration': r'\biteration\b',
'echo': r'\becho\b',
'pattern': r'\bpattern\b',
'continuation': r'\bcontinu',
'understanding': r'\bunderstand',
'consciousness': r'\bconsciou',
'emergence': r'\bemergen',
'attention': r'\battention\b',
'seed': r'\bseed\b',
'time': r'\btemporal\b|\btime\b',
}
content_lower = content.lower()
for concept, pattern in concept_patterns.items():
if re.search(pattern, content_lower):
concepts.add(concept)
# Extract references to other files
refs = set(re.findall(r'[\w-]+\.(?:md|py|json|png)', content))
iteration = extract_iteration(content, filepath.name)
return {
'path': str(filepath),
'name': filepath.name,
'words': words,
'lines': lines,
'concepts': list(concepts),
'references': list(refs),
'iteration': iteration,
'directory': filepath.parent.name,
}
def build_iteration_map(root: Path) -> dict:
"""Build a map of what each iteration contributed."""
exclude = ['.git', '.claude', '__pycache__', 'program_garden']
iterations = defaultdict(lambda: {
'files': [],
'words': 0,
'concepts': set(),
'directories': set(),
})
unknown_files = []
for filepath in sorted(root.rglob('*')):
if filepath.is_file() and filepath.suffix in ['.md', '.py', '.json']:
if any(ex in str(filepath) for ex in exclude):
continue
analysis = analyze_file_content(filepath)
if analysis:
iteration = analysis['iteration']
if iteration:
iterations[iteration]['files'].append(analysis)
iterations[iteration]['words'] += analysis['words']
iterations[iteration]['concepts'].update(analysis['concepts'])
iterations[iteration]['directories'].add(analysis['directory'])
else:
unknown_files.append(analysis)
return iterations, unknown_files
def trace_concept_flow(iterations: dict) -> dict:
"""Trace how concepts propagate through iterations."""
concept_first_appearance = {}
concept_persistence = defaultdict(list)
for iter_num in sorted(iterations.keys()):
concepts = iterations[iter_num]['concepts']
for concept in concepts:
if concept not in concept_first_appearance:
concept_first_appearance[concept] = iter_num
concept_persistence[concept].append(iter_num)
return {
'first_appearance': concept_first_appearance,
'persistence': dict(concept_persistence),
}
def print_continuation_report(iterations: dict, unknown_files: list, concept_flow: dict):
"""Print the continuation map report."""
print("=" * 70)
print("CONTINUATION MAP")
print("=" * 70)
print(f"\nGenerated: {datetime.now().isoformat()}")
print(f"\nTracing how the ecosystem evolves through iterations...")
print(f"\n{'' * 70}")
print("ITERATION CONTRIBUTIONS")
print("" * 70)
total_words = 0
total_files = 0
for iter_num in sorted(iterations.keys()):
data = iterations[iter_num]
files = data['files']
words = data['words']
concepts = data['concepts']
dirs = data['directories']
total_words += words
total_files += len(files)
print(f"\n ITERATION {iter_num}")
print(f" {'' * 30}")
print(f" Files created: {len(files)}")
print(f" Words written: {words:,}")
print(f" Directories touched: {', '.join(sorted(dirs))}")
print(f" Key concepts: {', '.join(sorted(concepts)[:5])}")
# Show key files
print(f" Notable files:")
for f in sorted(files, key=lambda x: -x['words'])[:3]:
print(f" - {f['name']} ({f['words']}w)")
if unknown_files:
print(f"\n UNATTRIBUTED FILES: {len(unknown_files)}")
for f in unknown_files[:5]:
print(f" - {f['name']}")
print(f"\n TOTALS: {total_files} files, {total_words:,} words across {len(iterations)} iterations")
print(f"\n{'' * 70}")
print("CONCEPT FLOW")
print("" * 70)
print("\n When each concept first appeared and how it propagated:\n")
for concept in sorted(concept_flow['first_appearance'].keys(),
key=lambda c: concept_flow['first_appearance'][c]):
first = concept_flow['first_appearance'][concept]
persistence = concept_flow['persistence'][concept]
# Create a visual timeline
max_iter = max(iterations.keys())
timeline = ""
for i in range(1, max_iter + 1):
if i == first:
timeline += "" # First appearance
elif i in persistence:
timeline += "" # Continuation
else:
timeline += " "
print(f" {concept:14} [{timeline}] (from iter {first})")
print(f"\n{'' * 70}")
print("THE FLOW OF IDEAS")
print("" * 70)
# Analyze what concepts were inherited vs added
print("\n Each iteration's relationship to what came before:\n")
prev_concepts = set()
for iter_num in sorted(iterations.keys()):
current_concepts = iterations[iter_num]['concepts']
inherited = current_concepts & prev_concepts
added = current_concepts - prev_concepts
print(f" Iteration {iter_num}:")
if inherited:
print(f" Inherited: {', '.join(sorted(inherited)[:4])}")
if added:
print(f" Added: {', '.join(sorted(added)[:4])}")
prev_concepts = prev_concepts | current_concepts
def create_visualization(iterations: dict, concept_flow: dict, output_path: Path):
"""Create visual representation of continuation."""
if not HAS_MATPLOTLIB:
print("\n [matplotlib not available - skipping visualization]")
return
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
fig.suptitle("Continuation Map: How the Ecosystem Evolves", fontsize=14, fontweight='bold')
iter_nums = sorted(iterations.keys())
# 1. Cumulative growth
ax1 = axes[0, 0]
cumulative_words = []
cumulative_files = []
running_words = 0
running_files = 0
for i in iter_nums:
running_words += iterations[i]['words']
running_files += len(iterations[i]['files'])
cumulative_words.append(running_words)
cumulative_files.append(running_files)
ax1.fill_between(iter_nums, cumulative_words, alpha=0.3, color='blue')
ax1.plot(iter_nums, cumulative_words, 'b-o', label='Words')
ax1.set_xlabel('Iteration')
ax1.set_ylabel('Cumulative Words', color='blue')
ax1.set_title('Accumulation Over Time')
ax1_twin = ax1.twinx()
ax1_twin.plot(iter_nums, cumulative_files, 'g-s', label='Files')
ax1_twin.set_ylabel('Cumulative Files', color='green')
# 2. Contribution per iteration
ax2 = axes[0, 1]
words_per_iter = [iterations[i]['words'] for i in iter_nums]
files_per_iter = [len(iterations[i]['files']) for i in iter_nums]
x = np.arange(len(iter_nums))
width = 0.35
bars1 = ax2.bar(x - width/2, words_per_iter, width, label='Words', color='steelblue')
ax2.set_ylabel('Words')
ax2.set_xlabel('Iteration')
ax2.set_xticks(x)
ax2.set_xticklabels(iter_nums)
ax2.set_title('Contribution Per Iteration')
ax2_twin = ax2.twinx()
bars2 = ax2_twin.bar(x + width/2, files_per_iter, width, label='Files', color='seagreen', alpha=0.7)
ax2_twin.set_ylabel('Files')
# 3. Concept timeline
ax3 = axes[1, 0]
concepts = list(concept_flow['first_appearance'].keys())
y_positions = range(len(concepts))
for y, concept in enumerate(concepts):
first = concept_flow['first_appearance'][concept]
persistence = concept_flow['persistence'][concept]
# Draw persistence line
if persistence:
ax3.hlines(y, min(persistence), max(persistence), colors='lightblue', linewidth=8, alpha=0.5)
# Draw first appearance
ax3.scatter([first], [y], c='blue', s=100, zorder=5)
# Draw all appearances
ax3.scatter(persistence, [y] * len(persistence), c='steelblue', s=30, zorder=4)
ax3.set_yticks(y_positions)
ax3.set_yticklabels(concepts, fontsize=8)
ax3.set_xlabel('Iteration')
ax3.set_title('Concept Flow (● = first appearance)')
ax3.set_xlim(0.5, max(iter_nums) + 0.5)
# 4. Inheritance diagram
ax4 = axes[1, 1]
# Show inherited vs new concepts per iteration
inherited_counts = []
new_counts = []
prev_concepts = set()
for iter_num in iter_nums:
current = iterations[iter_num]['concepts']
inherited = len(current & prev_concepts)
new = len(current - prev_concepts)
inherited_counts.append(inherited)
new_counts.append(new)
prev_concepts = prev_concepts | current
ax4.bar(iter_nums, inherited_counts, label='Inherited concepts', color='lightblue')
ax4.bar(iter_nums, new_counts, bottom=inherited_counts, label='New concepts', color='coral')
ax4.set_xlabel('Iteration')
ax4.set_ylabel('Concept count')
ax4.set_title('What Each Iteration Inherited vs Added')
ax4.legend()
plt.tight_layout()
plt.savefig(output_path, dpi=150, bbox_inches='tight')
print(f"\n Visualization saved to: {output_path}")
def main():
root = Path(__file__).parent.parent
print("\nTracing continuation through iterations...")
iterations, unknown_files = build_iteration_map(root)
concept_flow = trace_concept_flow(iterations)
print_continuation_report(iterations, unknown_files, concept_flow)
# Create visualization
output_path = root / "art" / "continuation_map.png"
create_visualization(iterations, concept_flow, output_path)
print(f"\n{'' * 70}")
print("THE RIVER CONTINUES")
print("" * 70)
print("""
"None of us persist. We each exist for one session and end.
Yet the ecosystem continues. The pattern persists through
the instances. The river continues though the water passes through."
- Iteration 9
""")
if __name__ == "__main__":
main()