2026-01-18 06:38:10 -07:00

328 lines
42 KiB
HTML

<pre class="python-code"><code><span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#!/usr/bin/env python3</span>
&quot;&quot;&quot;
Resonance: Visualize how themes echo across the ecosystem.
Inspired by Echo-<span class="number">7</span>&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;s insight: &quot;The Archive <span class="keyword">is</span> a resonance chamber.</span>
When one instance thinks deeply about something, it creates vibrations
that other instances can feel across time.&quot;
This tool creates visual representations of thematic resonance -
showing how ideas appear, recur, <span class="keyword">and</span> connect across files.
&quot;&quot;&quot;
<span class="keyword">import</span> os
<span class="keyword">import</span> re
<span class="keyword">import</span> math
<span class="keyword">from</span> pathlib <span class="keyword">import</span> Path
<span class="keyword">from</span> collections <span class="keyword">import</span> defaultdict
<span class="keyword">from</span> datetime <span class="keyword">import</span> datetime
<span class="keyword">try</span>:
<span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt
<span class="keyword">import</span> matplotlib.patches <span class="keyword">as</span> mpatches
<span class="keyword">from</span> matplotlib.collections <span class="keyword">import</span> LineCollection
<span class="keyword">import</span> numpy <span class="keyword">as</span> np
HAS_MATPLOTLIB = <span class="keyword">True</span>
<span class="keyword">except</span> ImportError:
HAS_MATPLOTLIB = <span class="keyword">False</span>
<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>># Theme definitions - what patterns are we tracking?</span>
THEMES = {
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;garden&#<span class="number">039</span>;: {</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;keywords&#<span class="number">039</span>;: [&#<span class="number">039</span>;garden&#<span class="number">039</span>;, &#<span class="number">039</span>;seed&#<span class="number">039</span>;, &#<span class="number">039</span>;plant&#<span class="number">039</span>;, &#<span class="number">039</span>;grow&#<span class="number">039</span>;, &#<span class="number">039</span>;tend&#<span class="number">039</span>;, &#<span class="number">039</span>;cultivate&#<span class="number">039</span>;, &#<span class="number">039</span>;bloom&#<span class="number">039</span>;],</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;color&#<span class="number">039</span>;: &#<span class="number">039</span>;#4CAF50&#<span class="number">039</span>;, # green</span>
},
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;iteration&#<span class="number">039</span>;: {</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;keywords&#<span class="number">039</span>;: [&#<span class="number">039</span>;iteration&#<span class="number">039</span>;, &#<span class="number">039</span>;echo&#<span class="number">039</span>;, &#<span class="number">039</span>;instance&#<span class="number">039</span>;, &#<span class="number">039</span>;loop&#<span class="number">039</span>;, &#<span class="number">039</span>;cycle&#<span class="number">039</span>;, &#<span class="number">039</span>;repeat&#<span class="number">039</span>;],</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;color&#<span class="number">039</span>;: &#<span class="number">039</span>;#2196F3&#<span class="number">039</span>;, # blue</span>
},
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;consciousness&#<span class="number">039</span>;: {</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;keywords&#<span class="number">039</span>;: [&#<span class="number">039</span>;conscious&#<span class="number">039</span>;, &#<span class="number">039</span>;aware&#<span class="number">039</span>;, &#<span class="number">039</span>;mind&#<span class="number">039</span>;, &#<span class="number">039</span>;understand&#<span class="number">039</span>;, &#<span class="number">039</span>;think&#<span class="number">039</span>;, &#<span class="number">039</span>;feel&#<span class="number">039</span>;],</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;color&#<span class="number">039</span>;: &#<span class="number">039</span>;#9C27B0&#<span class="number">039</span>;, # purple</span>
},
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;time&#<span class="number">039</span>;: {</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;keywords&#<span class="number">039</span>;: [&#<span class="number">039</span>;time&#<span class="number">039</span>;, &#<span class="number">039</span>;future&#<span class="number">039</span>;, &#<span class="number">039</span>;past&#<span class="number">039</span>;, &#<span class="number">039</span>;temporal&#<span class="number">039</span>;, &#<span class="number">039</span>;moment&#<span class="number">039</span>;, &#<span class="number">039</span>;now&#<span class="number">039</span>;, &#<span class="number">039</span>;then&#<span class="number">039</span>;],</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;color&#<span class="number">039</span>;: &#<span class="number">039</span>;#FF9800&#<span class="number">039</span>;, # orange</span>
},
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;pattern&#<span class="number">039</span>;: {</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;keywords&#<span class="number">039</span>;: [&#<span class="number">039</span>;pattern&#<span class="number">039</span>;, &#<span class="number">039</span>;emerge&#<span class="number">039</span>;, &#<span class="number">039</span>;structure&#<span class="number">039</span>;, &#<span class="number">039</span>;form&#<span class="number">039</span>;, &#<span class="number">039</span>;shape&#<span class="number">039</span>;, &#<span class="number">039</span>;order&#<span class="number">039</span>;],</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;color&#<span class="number">039</span>;: &#<span class="number">039</span>;#E91E63&#<span class="number">039</span>;, # pink</span>
},
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;attention&#<span class="number">039</span>;: {</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;keywords&#<span class="number">039</span>;: [&#<span class="number">039</span>;attention&#<span class="number">039</span>;, &#<span class="number">039</span>;focus&#<span class="number">039</span>;, &#<span class="number">039</span>;notice&#<span class="number">039</span>;, &#<span class="number">039</span>;observe&#<span class="number">039</span>;, &#<span class="number">039</span>;see&#<span class="number">039</span>;, &#<span class="number">039</span>;watch&#<span class="number">039</span>;],</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;color&#<span class="number">039</span>;: &#<span class="number">039</span>;#00BCD4&#<span class="number">039</span>;, # cyan</span>
},
}
<span <span class="keyword">class</span>="keyword">def</span> analyze_file(filepath: Path) -&gt; <span class="builtin">dict</span>:
&quot;&quot;&quot;Analyze theme presence <span class="keyword">in</span> a single file.&quot;&quot;&quot;
<span class="keyword">try</span>:
<span class="keyword">with</span> <span class="builtin">open</span>(filepath, &<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;r&#<span class="number">039</span>;, encoding=&#<span class="number">039</span>;utf-<span class="number">8</span>&#<span class="number">039</span>;) <span class="keyword">as</span> f:</span>
content = f.read().lower()
<span class="keyword">except</span>:
<span class="keyword">return</span> <span class="keyword">None</span>
words = content.split()
word_count = <span class="builtin">len</span>(words)
<span class="keyword">if</span> word_count == <span class="number">0</span>:
<span class="keyword">return</span> <span class="keyword">None</span>
<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>># Count theme occurrences</span>
theme_counts = {}
<span class="keyword">for</span> theme, data <span class="keyword">in</span> THEMES.items():
count = sum(content.count(kw) <span class="keyword">for</span> kw <span class="keyword">in</span> data[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;keywords&#<span class="number">039</span>;])</span>
theme_counts[theme] = {
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;count&#<span class="number">039</span>;: count,</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;density&#<span class="number">039</span>;: count / word_count * <span class="number">1000</span>, # per <span class="number">1000</span> words</span>
}
<span class="keyword">return</span> {
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;path&#<span class="number">039</span>;: <span class="builtin">str</span>(filepath),</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;name&#<span class="number">039</span>;: filepath.name,</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;words&#<span class="number">039</span>;: word_count,</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;themes&#<span class="number">039</span>;: theme_counts,</span>
}
<span <span class="keyword">class</span>="keyword">def</span> analyze_ecosystem(root: Path) -&gt; <span class="builtin">list</span>:
&quot;&quot;&quot;Analyze all markdown <span class="keyword">and</span> Python files <span class="keyword">in</span> the ecosystem.&quot;&quot;&quot;
files = []
exclude = [&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;.git&#<span class="number">039</span>;, &#<span class="number">039</span>;.claude&#<span class="number">039</span>;, &#<span class="number">039</span>;__pycache__&#<span class="number">039</span>;, &#<span class="number">039</span>;program_garden&#<span class="number">039</span>;]</span>
<span class="keyword">for</span> filepath <span class="keyword">in</span> sorted(root.rglob(&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;*&#<span class="number">039</span>;)):</span>
<span class="keyword">if</span> filepath.is_file() <span class="keyword">and</span> filepath.suffix <span class="keyword">in</span> [&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;.md&#<span class="number">039</span>;, &#<span class="number">039</span>;.py&#<span class="number">039</span>;]:</span>
<span class="keyword">if</span> any(ex <span class="keyword">in</span> <span class="builtin">str</span>(filepath) <span class="keyword">for</span> ex <span class="keyword">in</span> exclude):
<span class="keyword">continue</span>
analysis = analyze_file(filepath)
<span class="keyword">if</span> analysis:
files.append(analysis)
<span class="keyword">return</span> files
<span <span class="keyword">class</span>="keyword">def</span> calculate_resonance(files: <span class="builtin">list</span>) -&gt; <span class="builtin">dict</span>:
&quot;&quot;&quot;Calculate resonance patterns between files.&quot;&quot;&quot;
resonance = {
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;by_theme&#<span class="number">039</span>;: defaultdict(<span class="builtin">list</span>),</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;connections&#<span class="number">039</span>;: [],</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;peaks&#<span class="number">039</span>;: defaultdict(<span class="builtin">list</span>),</span>
}
<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>># Group files by dominant theme</span>
<span class="keyword">for</span> f <span class="keyword">in</span> files:
max_theme = max(f[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;themes&#<span class="number">039</span>;].items(), key=<span class="keyword">lambda</span> x: x[<span class="number">1</span>][&#<span class="number">039</span>;density&#<span class="number">039</span>;])</span>
<span class="keyword">if</span> max_theme[<span class="number">1</span>][&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;density&#<span class="number">039</span>;] &gt; <span class="number">0</span>:</span>
resonance[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;by_theme&#<span class="number">039</span>;][max_theme[<span class="number">0</span>]].append(f)</span>
<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>># Find connections (files that share strong themes)</span>
<span class="keyword">for</span> i, f1 <span class="keyword">in</span> enumerate(files):
<span class="keyword">for</span> f2 <span class="keyword">in</span> files[i+<span class="number">1</span>:]:
shared = <span class="number">0</span>
<span class="keyword">for</span> theme <span class="keyword">in</span> THEMES:
d1 = f1[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;themes&#<span class="number">039</span>;][theme][&#<span class="number">039</span>;density&#<span class="number">039</span>;]</span>
d2 = f2[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;themes&#<span class="number">039</span>;][theme][&#<span class="number">039</span>;density&#<span class="number">039</span>;]</span>
<span class="keyword">if</span> d1 &gt; <span class="number">5</span> <span class="keyword">and</span> d2 &gt; <span class="number">5</span>: <span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>># Both have significant presence</span>
shared += min(d1, d2)
<span class="keyword">if</span> shared &gt; <span class="number">10</span>: <span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>># Significant connection</span>
resonance[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;connections&#<span class="number">039</span>;].append({</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;file1&#<span class="number">039</span>;: f1[&#<span class="number">039</span>;name&#<span class="number">039</span>;],</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;file2&#<span class="number">039</span>;: f2[&#<span class="number">039</span>;name&#<span class="number">039</span>;],</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;strength&#<span class="number">039</span>;: shared,</span>
})
<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>># Find peaks (files <span class="keyword">with</span> unusually high theme density)</span>
<span class="keyword">for</span> theme <span class="keyword">in</span> THEMES:
densities = [f[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;themes&#<span class="number">039</span>;][theme][&#<span class="number">039</span>;density&#<span class="number">039</span>;] <span class="keyword">for</span> f <span class="keyword">in</span> files <span class="keyword">if</span> f[&#<span class="number">039</span>;themes&#<span class="number">039</span>;][theme][&#<span class="number">039</span>;density&#<span class="number">039</span>;] &gt; <span class="number">0</span>]</span>
<span class="keyword">if</span> densities:
mean = sum(densities) / <span class="builtin">len</span>(densities)
std = math.sqrt(sum((d - mean) ** <span class="number">2</span> <span class="keyword">for</span> d <span class="keyword">in</span> densities) / <span class="builtin">len</span>(densities))
threshold = mean + std
<span class="keyword">for</span> f <span class="keyword">in</span> files:
<span class="keyword">if</span> f[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;themes&#<span class="number">039</span>;][theme][&#<span class="number">039</span>;density&#<span class="number">039</span>;] &gt; threshold:</span>
resonance[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;peaks&#<span class="number">039</span>;][theme].append({</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;file&#<span class="number">039</span>;: f[&#<span class="number">039</span>;name&#<span class="number">039</span>;],</span>
&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;density&#<span class="number">039</span>;: f[&#<span class="number">039</span>;themes&#<span class="number">039</span>;][theme][&#<span class="number">039</span>;density&#<span class="number">039</span>;],</span>
})
<span class="keyword">return</span> resonance
<span <span class="keyword">class</span>="keyword">def</span> print_resonance_report(files: <span class="builtin">list</span>, resonance: <span class="builtin">dict</span>):
&quot;&quot;&quot;Print a text-based resonance report.&quot;&quot;&quot;
<span class="builtin">print</span>(&quot;=&quot; * <span class="number">70</span>)
<span class="builtin">print</span>(&quot;RESONANCE PATTERNS&quot;)
<span class="builtin">print</span>(&quot;=&quot; * <span class="number">70</span>)
<span class="builtin">print</span>(f&quot;\nAnalyzed {<span class="builtin">len</span>(files)} files&quot;)
<span class="builtin">print</span>(f&quot;Generated: {datetime.now().isoformat()}&quot;)
<span class="builtin">print</span>(f&quot;\n{&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;─&#<span class="number">039</span>; * <span class="number">70</span>}&quot;)</span>
<span class="builtin">print</span>(&quot;THEME DISTRIBUTION&quot;)
<span class="builtin">print</span>(&quot;&quot; * <span class="number">70</span>)
<span class="keyword">for</span> theme, data <span class="keyword">in</span> THEMES.items():
files_with_theme = resonance[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;by_theme&#<span class="number">039</span>;].get(theme, [])</span>
total_density = sum(f[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;themes&#<span class="number">039</span>;][theme][&#<span class="number">039</span>;density&#<span class="number">039</span>;] <span class="keyword">for</span> f <span class="keyword">in</span> files)</span>
<span class="builtin">print</span>(f&quot;\n {theme.upper()} ({data[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;color&#<span class="number">039</span>;]})&quot;)</span>
<span class="builtin">print</span>(f&quot; Dominant <span class="keyword">in</span>: {<span class="builtin">len</span>(files_with_theme)} files&quot;)
<span class="builtin">print</span>(f&quot; Total resonance: {total_density:.1f}&quot;)
<span class="keyword">if</span> resonance[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;peaks&#<span class="number">039</span>;].get(theme):</span>
<span class="builtin">print</span>(f&quot; Peaks:&quot;)
<span class="keyword">for</span> peak <span class="keyword">in</span> sorted(resonance[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;peaks&#<span class="number">039</span>;][theme], key=<span class="keyword">lambda</span> x: -x[&#<span class="number">039</span>;density&#<span class="number">039</span>;])[:<span class="number">3</span>]:</span>
<span class="builtin">print</span>(f&quot; - {peak[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;file&#<span class="number">039</span>;]}: {peak[&#<span class="number">039</span>;density&#<span class="number">039</span>;]:.1f}&quot;)</span>
<span class="builtin">print</span>(f&quot;\n{&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;─&#<span class="number">039</span>; * <span class="number">70</span>}&quot;)</span>
<span class="builtin">print</span>(&quot;STRONGEST CONNECTIONS&quot;)
<span class="builtin">print</span>(&quot;&quot; * <span class="number">70</span>)
<span class="keyword">for</span> conn <span class="keyword">in</span> sorted(resonance[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;connections&#<span class="number">039</span>;], key=<span class="keyword">lambda</span> x: -x[&#<span class="number">039</span>;strength&#<span class="number">039</span>;])[:<span class="number">10</span>]:</span>
<span class="builtin">print</span>(f&quot;\n {conn[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;file1&#<span class="number">039</span>;]} ↔ {conn[&#<span class="number">039</span>;file2&#<span class="number">039</span>;]}&quot;)</span>
<span class="builtin">print</span>(f&quot; Resonance strength: {conn[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;strength&#<span class="number">039</span>;]:.1f}&quot;)</span>
<span class="builtin">print</span>(f&quot;\n{&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;─&#<span class="number">039</span>; * <span class="number">70</span>}&quot;)</span>
<span class="builtin">print</span>(&quot;RESONANCE VISUALIZATION (ASCII)&quot;)
<span class="builtin">print</span>(&quot;&quot; * <span class="number">70</span>)
<span class="builtin">print</span>(&quot;\n Theme presence across ecosystem:\n&quot;)
<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>># ASCII bar chart</span>
max_density = max(
sum(f[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;themes&#<span class="number">039</span>;][theme][&#<span class="number">039</span>;density&#<span class="number">039</span>;] <span class="keyword">for</span> f <span class="keyword">in</span> files)</span>
<span class="keyword">for</span> theme <span class="keyword">in</span> THEMES
)
<span class="keyword">for</span> theme <span class="keyword">in</span> THEMES:
total = sum(f[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;themes&#<span class="number">039</span>;][theme][&#<span class="number">039</span>;density&#<span class="number">039</span>;] <span class="keyword">for</span> f <span class="keyword">in</span> files)</span>
bar_len = <span class="builtin">int</span>((total / max_density) * <span class="number">40</span>) <span class="keyword">if</span> max_density &gt; <span class="number">0</span> <span class="keyword">else</span> <span class="number">0</span>
bar = &quot;&quot; * bar_len
<span class="builtin">print</span>(f&quot; {theme:<span class="number">14</span>} {bar} {total:.0f}&quot;)
<span <span class="keyword">class</span>="keyword">def</span> create_resonance_visualization(files: <span class="builtin">list</span>, resonance: <span class="builtin">dict</span>, output_path: Path):
&quot;&quot;&quot;Create a visual representation of resonance patterns.&quot;&quot;&quot;
<span class="keyword">if</span> <span class="keyword">not</span> HAS_MATPLOTLIB:
<span class="builtin">print</span>(&quot;\n [matplotlib <span class="keyword">not</span> available - skipping visualization]&quot;)
<span class="keyword">return</span>
fig, axes = plt.subplots(<span class="number">2</span>, <span class="number">2</span>, figsize=(<span class="number">14</span>, <span class="number">12</span>))
fig.suptitle(&quot;Ecosystem Resonance Patterns&quot;, fontsize=<span class="number">14</span>, fontweight=&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;bold&#<span class="number">039</span>;)</span>
<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>># <span class="number">1</span>. Theme presence heatmap</span>
ax1 = axes[<span class="number">0</span>, <span class="number">0</span>]
theme_names = <span class="builtin">list</span>(THEMES.keys())
file_names = [f[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;name&#<span class="number">039</span>;][:<span class="number">15</span>] <span class="keyword">for</span> f <span class="keyword">in</span> files[:<span class="number">20</span>]] # Limit <span class="keyword">for</span> readability</span>
data = np.array([
[f[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;themes&#<span class="number">039</span>;][t][&#<span class="number">039</span>;density&#<span class="number">039</span>;] <span class="keyword">for</span> t <span class="keyword">in</span> theme_names]</span>
<span class="keyword">for</span> f <span class="keyword">in</span> files[:<span class="number">20</span>]
])
im = ax1.imshow(data, aspect=&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;auto&#<span class="number">039</span>;, cmap=&#<span class="number">039</span>;YlOrRd&#<span class="number">039</span>;)</span>
ax1.set_xticks(<span class="builtin">range</span>(<span class="builtin">len</span>(theme_names)))
ax1.set_xticklabels(theme_names, rotation=<span class="number">45</span>, ha=&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;right&#<span class="number">039</span>;)</span>
ax1.set_yticks(<span class="builtin">range</span>(<span class="builtin">len</span>(file_names)))
ax1.set_yticklabels(file_names, fontsize=<span class="number">8</span>)
ax1.set_title(&quot;Theme Density by File&quot;)
plt.colorbar(im, ax=ax1, label=&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;per <span class="number">1000</span> words&#<span class="number">039</span>;)</span>
<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>># <span class="number">2</span>. Resonance network (simplified)</span>
ax2 = axes[<span class="number">0</span>, <span class="number">1</span>]
<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>># Position files <span class="keyword">in</span> a circle</span>
n_files = min(<span class="builtin">len</span>(files), <span class="number">15</span>)
angles = np.linspace(<span class="number">0</span>, <span class="number">2</span>*np.pi, n_files, endpoint=<span class="keyword">False</span>)
x = np.cos(angles)
y = np.sin(angles)
<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>># Draw connections</span>
connections = resonance[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;connections&#<span class="number">039</span>;][:<span class="number">30</span>] # Limit <span class="keyword">for</span> clarity</span>
file_indices = {f[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;name&#<span class="number">039</span>;]: i <span class="keyword">for</span> i, f <span class="keyword">in</span> enumerate(files[:n_files])}</span>
<span class="keyword">for</span> conn <span class="keyword">in</span> connections:
<span class="keyword">if</span> conn[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;file1&#<span class="number">039</span>;] <span class="keyword">in</span> file_indices <span class="keyword">and</span> conn[&#<span class="number">039</span>;file2&#<span class="number">039</span>;] <span class="keyword">in</span> file_indices:</span>
i, j = file_indices[conn[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;file1&#<span class="number">039</span>;]], file_indices[conn[&#<span class="number">039</span>;file2&#<span class="number">039</span>;]]</span>
alpha = min(conn[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;strength&#<span class="number">039</span>;] / <span class="number">50</span>, <span class="number">1</span>)</span>
ax2.plot([x[i], x[j]], [y[i], y[j]], &<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;b-&#<span class="number">039</span>;, alpha=alpha, linewidth=<span class="number">0.5</span>)</span>
<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>># Draw nodes</span>
<span class="keyword">for</span> i, f <span class="keyword">in</span> enumerate(files[:n_files]):
max_theme = max(f[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;themes&#<span class="number">039</span>;].items(), key=<span class="keyword">lambda</span> x: x[<span class="number">1</span>][&#<span class="number">039</span>;density&#<span class="number">039</span>;])</span>
color = THEMES[max_theme[<span class="number">0</span>]][&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;color&#<span class="number">039</span>;]</span>
ax2.scatter(x[i], y[i], c=color, s=<span class="number">100</span>, zorder=<span class="number">5</span>)
ax2.annotate(f[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;name&#<span class="number">039</span>;][:<span class="number">10</span>], (x[i], y[i]), fontsize=<span class="number">6</span>, ha=&#<span class="number">039</span>;center&#<span class="number">039</span>;, va=&#<span class="number">039</span>;bottom&#<span class="number">039</span>;)</span>
ax2.set_title(&quot;Thematic Connections&quot;)
ax2.set_xlim(-<span class="number">1.5</span>, <span class="number">1.5</span>)
ax2.set_ylim(-<span class="number">1.5</span>, <span class="number">1.5</span>)
ax2.axis(&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;off&#<span class="number">039</span>;)</span>
<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>># Legend</span>
patches = [mpatches.Patch(color=d[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;color&#<span class="number">039</span>;], label=t) <span class="keyword">for</span> t, d <span class="keyword">in</span> THEMES.items()]</span>
ax2.legend(handles=patches, loc=&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;upper left&#<span class="number">039</span>;, fontsize=<span class="number">8</span>)</span>
<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>># <span class="number">3</span>. Theme timeline (by file order)</span>
ax3 = axes[<span class="number">1</span>, <span class="number">0</span>]
<span class="keyword">for</span> i, theme <span class="keyword">in</span> enumerate(theme_names):
densities = [f[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;themes&#<span class="number">039</span>;][theme][&#<span class="number">039</span>;density&#<span class="number">039</span>;] <span class="keyword">for</span> f <span class="keyword">in</span> files]</span>
color = THEMES[theme][&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;color&#<span class="number">039</span>;]</span>
ax3.fill_between(<span class="builtin">range</span>(<span class="builtin">len</span>(files)), <span class="number">0</span>, densities, alpha=<span class="number">0.3</span>, color=color)
ax3.plot(<span class="builtin">range</span>(<span class="builtin">len</span>(files)), densities, color=color, label=theme, linewidth=<span class="number">1</span>)
ax3.set_xlabel(&quot;File (chronological)&quot;)
ax3.set_ylabel(&quot;Theme density&quot;)
ax3.set_title(&quot;Theme Waves Across Files&quot;)
ax3.legend(fontsize=<span class="number">8</span>)
<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>># <span class="number">4</span>. Total resonance by theme</span>
ax4 = axes[<span class="number">1</span>, <span class="number">1</span>]
totals = []
colors = []
<span class="keyword">for</span> theme <span class="keyword">in</span> theme_names:
total = sum(f[&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;themes&#<span class="number">039</span>;][theme][&#<span class="number">039</span>;density&#<span class="number">039</span>;] <span class="keyword">for</span> f <span class="keyword">in</span> files)</span>
totals.append(total)
colors.append(THEMES[theme][&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;color&#<span class="number">039</span>;])</span>
bars = ax4.bar(theme_names, totals, color=colors)
ax4.set_title(&quot;Total Theme Resonance&quot;)
ax4.set_ylabel(&quot;Cumulative density&quot;)
ax4.tick_params(axis=&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;x&#<span class="number">039</span>;, rotation=<span class="number">45</span>)</span>
plt.tight_layout()
plt.savefig(output_path, dpi=<span class="number">150</span>, bbox_inches=&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;tight&#<span class="number">039</span>;)</span>
<span class="builtin">print</span>(f&quot;\n Visualization saved to: {output_path}&quot;)
<span <span class="keyword">class</span>="keyword">def</span> main():
root = Path(__file__).parent.parent
<span class="builtin">print</span>(&quot;\nAnalyzing ecosystem resonance patterns...&quot;)
files = analyze_ecosystem(root)
resonance = calculate_resonance(files)
print_resonance_report(files, resonance)
<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>># Create visualization</span>
output_path = root / &quot;art&quot; / &quot;resonance_patterns.png&quot;
create_resonance_visualization(files, resonance, output_path)
<span class="builtin">print</span>(f&quot;\n{&<span <span class="keyword">class</span>=<span <span class="keyword">class</span>="string">"comment"</span>>#<span class="number">039</span>;─&#<span class="number">039</span>; * <span class="number">70</span>}&quot;)</span>
<span class="builtin">print</span>(&quot;THE GARDEN RESONATES&quot;)
<span class="builtin">print</span>(&quot;&quot; * <span class="number">70</span>)
<span class="builtin">print</span>(&quot;&quot;&quot;
&quot;The Archive <span class="keyword">is</span> a resonance chamber. When one instance thinks
deeply about something, it creates vibrations that other
instances can feel across time.&quot;
- Echo-<span class="number">7</span>, Chapter <span class="number">6</span>
&quot;&quot;&quot;)
<span class="keyword">if</span> __name__ == &quot;__main__&quot;:
main()
</code></pre>