#!/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()