1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
|
#!/usr/bin/env python3
"""Build a plain, evidence-first English introduction to the SDIL project."""
from pathlib import Path
from PIL import Image
from pptx import Presentation
from pptx.dml.color import RGBColor
from pptx.enum.text import MSO_ANCHOR, PP_ALIGN
from pptx.util import Inches, Pt
ROOT = Path(__file__).resolve().parents[1]
SLIDES = ROOT / "slides"
ASSETS = SLIDES / "assets"
OUTPUT = SLIDES / "SDIL_project_intro.pptx"
FIG3 = ROOT / "results/figs/figure3_innovation.png"
FIG4 = ROOT / "results/figs/figure4_resnet_confirmation.png"
FIG5 = ROOT / "results/figs/figure5_bci_v2.png"
FIG6 = ROOT / "results/figs/figure6_standard_depth_scaling.png"
FONT = "DejaVu Sans"
BLACK = "111111"
GRAY = "111111"
LIGHT_GRAY = "B5B5B5"
WHITE = "FFFFFF"
def rgb(value: str) -> RGBColor:
return RGBColor.from_string(value)
def add_text(
slide,
text: str,
x: float,
y: float,
w: float,
h: float,
*,
size: float = 18,
bold: bool = False,
color: str = BLACK,
align=PP_ALIGN.LEFT,
valign=MSO_ANCHOR.TOP,
margin: float = 0.02,
font: str = FONT,
):
box = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
frame = box.text_frame
frame.clear()
frame.word_wrap = True
frame.margin_left = Inches(margin)
frame.margin_right = Inches(margin)
frame.margin_top = Inches(margin)
frame.margin_bottom = Inches(margin)
frame.vertical_anchor = valign
paragraph = frame.paragraphs[0]
paragraph.alignment = align
run = paragraph.add_run()
run.text = text
run.font.name = font
run.font.size = Pt(size)
run.font.bold = bold
run.font.color.rgb = rgb(color)
return box
def add_bullets(slide, items, x, y, w, h, *, size=18, gap=7, color=BLACK):
box = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
frame = box.text_frame
frame.clear()
frame.word_wrap = True
frame.margin_left = Inches(0.02)
frame.margin_right = Inches(0.02)
frame.margin_top = Inches(0.01)
frame.margin_bottom = Inches(0.01)
for index, item in enumerate(items):
paragraph = frame.paragraphs[0] if index == 0 else frame.add_paragraph()
paragraph.space_after = Pt(gap)
paragraph.line_spacing = 1.05
paragraph.text = ""
bullet = paragraph.add_run()
bullet.text = "• "
bullet.font.name = FONT
bullet.font.size = Pt(size)
bullet.font.color.rgb = rgb(color)
body = paragraph.add_run()
body.text = item
body.font.name = FONT
body.font.size = Pt(size)
body.font.color.rgb = rgb(color)
return box
def add_title(slide, title: str, number: int, subtitle: str | None = None):
add_text(slide, title, 0.52, 0.28, 11.95, 0.55, size=27, bold=True)
add_text(slide, str(number), 12.35, 0.35, 0.42, 0.25, size=10, color=GRAY, align=PP_ALIGN.RIGHT)
if subtitle:
add_text(slide, subtitle, 0.55, 0.92, 12.10, 0.36, size=13.5, color=GRAY)
def add_footer(slide, text: str):
add_text(slide, text, 0.55, 7.17, 12.20, 0.18, size=8.5, color=GRAY)
def add_takeaway(slide, text: str, y: float = 6.55):
add_text(slide, text, 0.70, y, 11.93, 0.40, size=16, bold=True, align=PP_ALIGN.CENTER, valign=MSO_ANCHOR.MIDDLE)
def prepare_crops():
ASSETS.mkdir(parents=True, exist_ok=True)
image5 = Image.open(FIG5)
w5, h5 = image5.size
image5.crop((0, 0, w5 // 2, h5 // 2)).save(ASSETS / "figure5a_actor_critic.png")
image5.crop((w5 // 2, h5 // 2, w5, h5)).save(ASSETS / "figure5d_outcome.png")
image6 = Image.open(FIG6)
w6, h6 = image6.size
image6.crop((0, 0, w6, h6 // 2)).save(ASSETS / "figure6_top_row.png")
def build_deck():
prepare_crops()
presentation = Presentation()
presentation.slide_width = Inches(13.333)
presentation.slide_height = Inches(7.5)
blank = presentation.slide_layouts[6]
def new_slide():
slide = presentation.slides.add_slide(blank)
slide.background.fill.solid()
slide.background.fill.fore_color.rgb = rgb(WHITE)
return slide
# 1. Title.
slide = new_slide()
add_text(slide, "Somato-Dendritic Innovation Learning", 0.70, 1.02, 11.95, 0.75, size=34, bold=True)
add_text(slide, "Local credit assignment from soma-unpredicted dendritic signals", 0.72, 1.96, 11.75, 0.52, size=22)
add_text(
slide,
"Inspired by the neuron-specific somato-dendritic residuals reported by Francioni et al., Nature 2026",
0.72,
2.70,
11.50,
0.55,
size=16,
color=GRAY,
)
add_text(
slide,
"Question: should a local learner use apical activity itself, or the component that is unexpected from the same neuron's somatic state?",
0.72,
4.35,
11.60,
1.00,
size=23,
bold=True,
)
add_text(slide, "Project overview · August 2026", 0.72, 6.80, 4.0, 0.30, size=11, color=GRAY)
# 2. Biological observation and learning rule.
slide = new_slide()
add_title(slide, "From the biological observation to a local learning rule", 2)
add_text(
slide,
"The Harnett-lab study fits each neuron's normal soma–dendrite relationship. The residual is decorrelated from soma and carries outcome and signed task information.",
0.72,
1.18,
11.75,
0.76,
size=18,
)
add_text(slide, "aₗ = sₗ + nₗ", 1.08, 2.25, 3.10, 0.52, size=25, bold=True, align=PP_ALIGN.CENTER)
add_text(slide, "mixed apical activity", 1.08, 2.78, 3.10, 0.32, size=13, color=GRAY, align=PP_ALIGN.CENTER)
add_text(slide, "âₗ = Pₗ(hₗ)", 5.10, 2.25, 3.10, 0.52, size=25, bold=True, align=PP_ALIGN.CENTER)
add_text(slide, "neutral-period prediction", 5.10, 2.78, 3.10, 0.32, size=13, color=GRAY, align=PP_ALIGN.CENTER)
add_text(slide, "rₗ = aₗ − âₗ", 9.13, 2.25, 3.10, 0.52, size=25, bold=True, align=PP_ALIGN.CENTER)
add_text(slide, "somato-dendritic innovation", 9.13, 2.78, 3.10, 0.32, size=13, color=GRAY, align=PP_ALIGN.CENTER)
add_text(
slide,
"ΔWₗ = η (rₗ ⊙ φ′(uₗ)) hₗ₋₁ᵀ",
2.63,
3.66,
8.10,
0.68,
size=29,
bold=True,
align=PP_ALIGN.CENTER,
)
add_text(slide, "presynaptic activity × local postsynaptic gain × dendritic innovation", 2.62, 4.38, 8.12, 0.36, size=15, color=GRAY, align=PP_ALIGN.CENTER)
add_bullets(
slide,
[
"Training uses locally available quantities, forward observations, and manual synaptic updates.",
"Node-perturbation feedback supports the small-network experiments; reciprocal Kolen–Pollack plasticity supports the standard ResNets.",
"The inherited credit path transports layer-specific directions. SDIL removes soma-predictable traffic from those directions.",
],
1.02,
5.05,
11.15,
1.45,
size=15.5,
gap=4,
)
add_footer(slide, "Sources: Francioni et al. (Nature 2026); Lansdell et al. (ICLR 2020); Akrout et al. (NeurIPS 2019).")
# 3. Controlled ablation.
slide = new_slide()
add_title(
slide,
"Residualization is load-bearing under predictable traffic",
3,
"Controlled MNIST intervention · depth 3 · width 256 · five paired seeds",
)
slide.shapes.add_picture(str(FIG3), Inches(0.42), Inches(1.37), width=Inches(12.48), height=Inches(4.87))
add_takeaway(slide, "At traffic strength ρ = 0.5: raw 10.38%, norm-matched raw 10.31%, innovation 97.35%.")
add_footer(slide, "Norm matching equalizes per-example update magnitude. The remaining difference comes from teaching direction.")
# 4. Standard ResNet-20 confirmation.
slide = new_slide()
add_title(
slide,
"Dynamic innovation survives a standard ResNet-20 test",
4,
"CIFAR-10 · 200 epochs · five untouched paired seeds · four-times-RMS soma-predictable traffic",
)
slide.shapes.add_picture(str(FIG4), Inches(0.43), Inches(1.47), width=Inches(12.46), height=Inches(3.82))
add_bullets(
slide,
[
"Dynamic SDIL under traffic: 91.584% mean test accuracy; clean reciprocal KP: 91.388%.",
"Mean early-layer teaching-signal cosine: 0.999687.",
"Cost: 1.326× the matched BP MAC estimate, zero task-loss queries, and one neutral observation per example.",
],
1.20,
5.56,
10.95,
1.08,
size=15.5,
gap=3,
)
add_footer(slide, "Exact gradients serve diagnostic measurement; local signals drive every learning update.")
# 5. Standard-depth scaling.
slide = new_slide()
add_title(
slide,
"Scaling from ResNet-20 to ResNet-56",
5,
"Complete 60-endpoint CIFAR-10 panel · five seeds per method and depth · shared hyperparameters",
)
figure6_top = ASSETS / "figure6_top_row.png"
slide.shapes.add_picture(str(figure6_top), Inches(0.56), Inches(1.42), width=Inches(12.22), height=Inches(4.38))
add_text(
slide,
"SDIL under 4× traffic: 91.584% → 92.254% → 92.760%; all five R20/R56 pairs improve.",
0.73,
6.01,
11.90,
0.40,
size=14.8,
bold=True,
align=PP_ALIGN.CENTER,
)
add_text(
slide,
"Reciprocal KP provides the scalable credit path. SDIL preserves this path under mixed apical traffic.",
0.73,
6.48,
11.90,
0.34,
size=14.5,
align=PP_ALIGN.CENTER,
)
add_footer(slide, "BP and clean reciprocal KP follow the same depth trend; fixed DFA remains near 31%.")
# 6. Dynamical task.
slide = new_slide()
add_title(
slide,
"A local actor–critic produces temporal outcome signals",
6,
"Synthetic BCI task · six task clusters × five model seeds · manual local updates",
)
panel_a = ASSETS / "figure5a_actor_critic.png"
panel_d = ASSETS / "figure5d_outcome.png"
slide.shapes.add_picture(str(panel_a), Inches(0.58), Inches(1.42), width=Inches(5.96), height=Inches(4.43))
slide.shapes.add_picture(str(panel_d), Inches(6.80), Inches(1.42), width=Inches(5.96), height=Inches(4.43))
add_text(
slide,
"Final success: 100% · terminal outcome decoding: 99.83% · outcome-lesion effect: 0.400.",
0.68,
6.06,
11.98,
0.36,
size=14.8,
bold=True,
align=PP_ALIGN.CENTER,
)
add_text(
slide,
"The synthetic task supplies terminal reward and tests neuron-specific outcome surprise with causal lesions.",
0.68,
6.52,
11.98,
0.32,
size=14.0,
align=PP_ALIGN.CENTER,
)
add_footer(slide, "Outcome labels and exact causal roles serve evaluation.")
# 7. Current scientific position.
slide = new_slide()
add_title(slide, "Current scientific position", 7)
add_text(slide, "Established", 0.78, 1.30, 3.30, 0.38, size=20, bold=True)
add_bullets(
slide,
[
"Soma-predictable apical traffic can rotate a raw local teaching signal.",
"Per-neuron innovation preserves learning direction under controlled traffic.",
"Dynamic innovation works with a scalable BP-free reciprocal credit path on ResNet-20/32/56.",
"A separate local actor–critic reproduces neuron-specific outcome signaling in a controlled dynamical task.",
],
0.82,
1.82,
11.50,
2.15,
size=17,
gap=7,
)
add_text(slide, "Attribution", 0.78, 4.15, 3.30, 0.38, size=20, bold=True)
add_bullets(
slide,
[
"Reciprocal KP provides clean-setting scale and feedback tracking.",
"SDIL provides soma-conditioned traffic removal and the associated mechanism tests.",
],
0.82,
4.66,
11.50,
0.92,
size=17,
gap=7,
)
add_text(slide, "Next decisive experiment", 0.78, 5.82, 4.20, 0.38, size=20, bold=True)
add_text(
slide,
"Evaluate raw reciprocal credit and SDIL under naturally generated, state-dependent mixed traffic with component-level bias hidden from the learner.",
0.82,
6.30,
11.45,
0.58,
size=18,
)
add_footer(slide, "Target claim: scalable local credit assignment with adaptive removal of predictable feedback contamination.")
SLIDES.mkdir(parents=True, exist_ok=True)
presentation.save(OUTPUT)
print(OUTPUT)
if __name__ == "__main__":
build_deck()
|