Compare commits

...

14 Commits

Author SHA1 Message Date
Daniel Alves Rösel
a5d5919b40 Merge pull request #58 from velocitatem/defense
Defense
2026-06-10 21:12:54 +02:00
01aac1d3c2 facupdqate 2026-06-10 21:08:27 +02:00
38eff1a4cc updating page 2026-06-10 21:07:30 +02:00
b8b8748531 chore 2026-06-10 16:51:06 +02:00
b81d5f231f adding to readme 2026-06-10 16:49:24 +02:00
8c8a810a92 updating website with defense assets 2026-05-19 11:37:01 +02:00
fec9aa24fb complied no errors 2026-05-12 12:48:05 +02:00
172383b59f compiled 2026-05-12 12:47:50 +02:00
628ffdc464 fixing colors 2026-05-12 12:47:41 +02:00
0b1f59e49f stylized defense 2026-04-30 10:12:16 +02:00
b677e80b80 early emojification 2026-04-27 17:45:40 +02:00
acf5bb5409 updating build 2026-04-24 11:44:20 +02:00
9bbacd6bdc cleaning the text from slides 2026-04-23 10:05:07 +02:00
29920aa56c preparing defense content pushing 2026-04-22 14:22:41 +02:00
30 changed files with 5386 additions and 146 deletions

4
.gitignore vendored
View File

@@ -64,8 +64,8 @@ tests/e2e/test-results/
tests/e2e/node_modules/**
# rl/sim run outputs
sim/rl/behavior_loader/*.dot
sim/rl/behavior_loader/*.png
# sim/rl/behavior_loader/*.dot
# sim/rl/behavior_loader/*.png
sim/rl/behavior_loader/*.svg
sim/rl/behavior_loader/*.pdf
sim/rl/runs/

View File

@@ -44,7 +44,7 @@ SWEEP_ENV_LOAD = set -a; [ -f "$(SWEEP_ENV_FILE)" ] && . "$(SWEEP_ENV_FILE)" ||
.PHONY: help
help:
@echo "pdf.build pdf.watch pdf.clean pdf.genpop pdf.genpop.watch pdf.summary pdf.summary.watch pdf.arxiv | test.backend test.e2e test.all | web.dev | install | train | benchmark | benchmark.simple | benchmark.agent | train.agent | train.bootstrap | stats.lines | docs.platform | manim.defense manim.defense.hq manim.render manim.render.full manim.render.poster manim.render.appendix manim.render.all"
@echo "pdf.build pdf.watch pdf.clean pdf.genpop pdf.genpop.watch pdf.summary pdf.summary.watch pdf.arxiv pdf.defense pdf.defense.html | test.backend test.e2e test.all | web.dev | install | train | benchmark | benchmark.simple | benchmark.agent | train.agent | train.bootstrap | stats.lines | docs.platform | manim.defense manim.defense.hq manim.render manim.render.full manim.render.poster manim.render.appendix manim.render.all"
@echo "backend.server backend.provider backend.worker | platform.up platform.down platform.logs | docker.train.publish"
@echo "data.pull data.push data.whoclicked.publish | study.margin-erosion study.margin-erosion.quick study.margin-erosion.plot"
@echo "tpu.ray.bootstrap tpu.ray.deps tpu.ray.verify tpu.ray.teardown"
@@ -110,6 +110,14 @@ pdf.summary:
pdf.summary.watch:
@bash scripts/nx_paper.sh watch-summary
.PHONY: pdf.defense
pdf.defense:
@cd paper/defense && pdflatex -interaction=nonstopmode defense.tex && pdflatex -interaction=nonstopmode defense.tex
.PHONY: pdf.defense.html
pdf.defense.html:
@bash paper/defense/build_html.sh
.PHONY: test.backend
test.backend:
@$(NX) run research:test

View File

@@ -1,8 +1,8 @@
<p align="center">
<img width="180" src="https://github.com/user-attachments/assets/d148b00d-e9f9-4280-89cc-0cc866e17251" alt="PHANTOM logo" />
</p>
<!-- <p align="center"> -->
<!-- <img width="180" src="https://github.com/user-attachments/assets/d148b00d-e9f9-4280-89cc-0cc866e17251" alt="PHANTOM logo" /> -->
<!-- </p> -->
# PHANTOM
![](./banner.png)
Agent-aware dynamic pricing research platform for studying how automated transaction orchestration changes pricing power, and for testing defenses that recover margin while protecting legitimate user experience.

BIN
banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

View File

@@ -45,8 +45,8 @@
<meta name="citation_pdf_url" content="https://pub-d5b94a3c29fd40c6b3881946e463fdb7.r2.dev/thesis-latest.pdf">
<!-- Additional SEO -->
<meta name="theme-color" content="#303030">
<meta name="msapplication-TileColor" content="#303030">
<meta name="theme-color" content="#1f2a38">
<meta name="msapplication-TileColor" content="#1f2a38">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
@@ -59,12 +59,19 @@
<title>PHANTOM: Pricing Heuristics Against Non-human Transaction Orchestration Mechanisms - Daniel Rösel | Academic Research</title>
<!-- Favicon and App Icons -->
<link rel="icon" type="image/svg+xml" href="static/images/favicon.svg">
<link rel="icon" type="image/x-icon" href="static/images/favicon.ico">
<link rel="apple-touch-icon" href="static/images/favicon.ico">
<link rel="apple-touch-icon" href="static/images/apple-touch-icon.png">
<!-- Critical CSS - Load synchronously -->
<link rel="stylesheet" href="static/css/bulma.min.css">
<link rel="stylesheet" href="static/css/index.css">
<link rel="stylesheet" href="static/css/defense-theme.css">
<!-- Defense-style monospace tagline font -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<!-- Non-critical CSS - Load asynchronously -->
<link rel="preload" href="static/css/bulma-carousel.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
@@ -236,83 +243,57 @@
</div>
<main id="main-content">
<section class="hero">
<section class="hero defense-cover" id="top">
<div class="hero-body">
<div class="container is-max-desktop">
<div class="columns is-centered">
<div class="column has-text-centered">
<h1 class="title is-1 publication-title">Pricing Heuristics Against Non-human Transaction Orchestration Mechanisms</h1>
<div class="is-size-5 publication-authors author-names">
<span class="author-block">
<a href="https://alves.world" target="_blank">Daniel Rösel</a></span>
<div class="defense-hero-grid">
<div class="defense-copy">
<p class="defense-kicker">IE University Bachelor's Thesis · 2025</p>
<h1 class="title publication-title defense-title">PHANTOM</h1>
<p class="defense-subtitle">Revenue management in the age of <span class="mark">AI agents</span>.</p>
<div class="defense-chip-row" aria-label="Core thesis dimensions">
<span class="defense-chip">Private Valuation</span>
<span class="defense-chip">True Demand</span>
<span class="defense-chip">Constraints</span>
</div>
<div class="is-size-5 publication-authors author-meta">
<span class="author-block">IE University<br>Bachelor's Thesis 2025</span>
<span class="eql-cntrb">Advisor: Alberto Martín Izquierdo</span>
<div class="defense-meta-card" aria-label="Project authorship">
<span>Written by Daniel Rösel</span>
<span class="dot" aria-hidden="true"></span>
<span>Advised by Alberto Martín Izquierdo</span>
</div>
<div class="column has-text-centered">
<div class="publication-links">
<span class="link-block">
<a href="https://blog.alves.world/series/phantom" target="_blank"
class="external-link button is-normal is-rounded is-dark">
<span class="icon">
<i class="fas fa-blog"></i>
</span>
<span>Blog Series</span>
<div class="defense-links publication-links" aria-label="Project links">
<a href="https://pub-d5b94a3c29fd40c6b3881946e463fdb7.r2.dev/thesis-latest.pdf" target="_blank" class="external-link button is-normal is-rounded is-dark">
<span class="icon"><i class="fas fa-file-pdf"></i></span>
<span>Thesis</span>
</a>
</span>
<span class="link-block">
<a href="https://pub-d5b94a3c29fd40c6b3881946e463fdb7.r2.dev/thesis-latest.pdf" target="_blank"
class="external-link button is-normal is-rounded is-dark">
<span class="icon">
<i class="fas fa-file-pdf"></i>
</span>
<span>Paper</span>
</a>
</span>
<span class="link-block">
<a href="https://huggingface.co/datasets/velocitatem/whoclickedit" target="_blank"
class="external-link button is-normal is-rounded is-dark">
<span class="icon">
<i class="fas fa-database"></i>
</span>
<span>Dataset</span>
</a>
</span>
<span class="link-block">
<a href="https://github.com/velocitatem/PHANTOM" target="_blank"
class="external-link button is-normal is-rounded is-dark">
<span class="icon">
<i class="fab fa-github"></i>
</span>
<a href="https://github.com/velocitatem/PHANTOM" target="_blank" class="external-link button is-normal is-rounded is-dark">
<span class="icon"><i class="fab fa-github"></i></span>
<span>Code</span>
</a>
</span>
<span class="link-block">
<a href="https://phantom-hotel.vercel.app" target="_blank"
class="external-link button is-normal is-rounded is-dark">
<span class="icon">
<i class="fas fa-globe"></i>
</span>
<span>Hotel Demo</span>
<a href="https://huggingface.co/datasets/velocitatem/whoclickedit" target="_blank" class="external-link button is-normal is-rounded is-dark">
<span class="icon"><i class="fas fa-database"></i></span>
<span>Dataset</span>
</a>
</span>
<span class="link-block">
<a href="https://phantom-airline.vercel.app" target="_blank"
class="external-link button is-normal is-rounded is-dark">
<span class="icon">
<i class="fas fa-plane"></i>
</span>
<span>Airline Demo</span>
<a href="documentation/" class="external-link button is-normal is-rounded is-light-outline">
<span class="icon"><i class="fas fa-book"></i></span>
<span>Docs</span>
</a>
</span>
</div>
<p class="tpu-credit">Powered by <span class="accent">Google</span> TPU Research Cloud.</p>
</div>
<div class="defense-visual" aria-hidden="true">
<div class="defense-orbit-card">
<div class="defense-art-stack">
<img class="agent-art" src="static/images/agent.svg" alt="" loading="eager">
<span class="mini-token"><i class="fas fa-dollar-sign"></i></span>
<span class="mini-token"><i class="fas fa-wave-square"></i></span>
<span class="mini-token"><i class="fas fa-shield-alt"></i></span>
</div>
</div>
</div>
</div>
@@ -320,8 +301,29 @@
</div>
</section>
<section class="defense-overview-strip" aria-label="PHANTOM defense overview">
<div class="container is-max-desktop">
<div class="defense-overview-grid">
<article class="defense-overview-card">
<span class="num">01</span>
<h3>The vulnerability</h3>
<p>Repeated agent price queries collapse the Cost of Information that dynamic pricing depends on.</p>
</article>
<article class="defense-overview-card">
<span class="num">02</span>
<h3>The signal</h3>
<p>Human and agent sessions separate through transition-kernel behavior, not brittle bot flags.</p>
</article>
<article class="defense-overview-card">
<span class="num">03</span>
<h3>The defense</h3>
<p>Distributionally robust RL preserves pricing power under contaminated demand.</p>
</article>
</div>
</div>
</section>
<section class="hero teaser">
<section class="hero teaser defense-teaser">
<div class="container is-max-desktop">
<div class="hero-body">
<div class="publication-banner">
@@ -337,7 +339,7 @@
<div class="container is-max-desktop">
<div class="columns is-centered has-text-centered">
<div class="column is-four-fifths">
<h2 class="title is-3">Abstract</h2>
<h2 class="title is-3">The thesis, compressed.</h2>
<div class="content has-text-justified">
<p>
Dynamic pricing extracts margin by exploiting the gap between what a platform knows and what a buyer knows. A user who browses a hotel across several sessions signals intent; the platform raises the price accordingly. That information asymmetry — the <em>Cost of Information</em> — is the economic engine behind session-based pricing in travel, hospitality, and e-commerce.
@@ -355,37 +357,106 @@
</section>
<!-- End paper abstract -->
<section class="section">
<!-- Defense-styled: new interaction environment (actor triptych) -->
<section class="section defense-block">
<div class="container is-max-desktop">
<div class="content has-text-justified">
<h2 class="title is-3 has-text-centered">How it works</h2>
<p>
The methodology runs in three stages: observe, distinguish, defend.
</p>
<h3 class="title is-4">Stage 1 — Observe</h3>
<p>
Both human participants and LLM agents are assigned goal-driven tasks on a live instrumented storefront (hotel or airline mode). Every interaction is logged as a timestamped event tuple <code>(action, item, timestamp)</code>. Actions are partitioned into four semantic categories — cart, dwell, navigation, filter — with decreasing signal weights (4.0, 2.0, 1.0, 0.5) calibrated by the KL divergence between human and agent transition rows. Price quotes are streamed to a separate Kafka topic, enabling joint analysis of behavior and pricing exposure. The platform runs a surge-discount heuristic during collection to expose participants to state-dependent prices.
</p>
<h3 class="title is-4">Stage 2 — Distinguish</h3>
<p>
From the labeled session trajectories, we estimate class-specific Markov transition kernels <em><sub>H</sub></em> and <em><sub>A</sub></em> by maximum likelihood. For any new partial trajectory τ', we compute KL divergence to each prototype:
</p>
<p style="text-align:center; font-style:italic; margin: 1rem 0;">
Δ<sub>H</sub> = D<sub>KL</sub>(T̂' ∥ T̄<sub>H</sub>), &nbsp; Δ<sub>A</sub> = D<sub>KL</sub>(T̂' ∥ T̄<sub>A</sub>)
</p>
<p>
The gap score <em>g</em>(τ') = Δ<sub>H</sub> Δ<sub>A</sub> maps to a weak agent probability via a temperature-controlled logistic function: <em>f</em>(τ') = σ((Δ<sub>H</sub> Δ<sub>A</sub>) / T). This is a continuous signal, not a binary bot flag. The MannWhitney test on gap scores between the 13-human and 16-agent cohorts yields U = 2.0, p = 0.0006 — the behavioral distributions are well separated.
</p>
<h3 class="title is-4">Stage 3 — Defend</h3>
<p>
A contamination generator G(α) mixes real human trajectories with synthetic agent trajectories drawn from <em><sub>A</sub></em> to produce training distributions at any contamination level α ∈ [0, 1]. The pricing policy is trained as a Stackelberg leader against a Wasserstein ambiguity set around the generator's empirical distribution, minimizing worst-case regret over plausible demand shifts. The per-step reward penalizes COI leakage — weighted by <em>f</em>(τ') — while a UX index bounds harm to legitimate users. Sweeps ran across 384 TPU chips (v4, v5e, v6e Trillium) covering six contamination levels and multiple algorithm variants (PPO, A2C, DQN, Q-table).
</p>
<h2 class="defense-heading">New interaction environment of <span class="mark">future commerce</span>.</h2>
<div class="actor-grid">
<div class="actor-card">
<div class="actor-art">
<img src="static/images/human.svg" alt="Isometric illustration of a human user as a cube character" loading="lazy" />
</div>
<h3>Users</h3>
<p>Have new needs and <strong>means of research</strong> &amp; acquisition.</p>
</div>
<div class="actor-card">
<div class="actor-art">
<img src="static/images/agent.svg" alt="Isometric illustration of an LLM agent depicted as a cube robot" loading="lazy" />
</div>
<h3>Agents</h3>
<p>Use browsers (C/BUA) to look human and create <strong>clean sessions</strong>.</p>
</div>
<div class="actor-card">
<div class="actor-art">
<div class="actor-icon" aria-hidden="true"><i class="fas fa-store"></i></div>
</div>
<h3>Platforms</h3>
<p>Run <strong>standard pricing</strong> algorithms and experience revenue loss.</p>
</div>
</div>
</div>
</section>
<!-- End actor triptych -->
<!-- Defense-styled: COI vulnerability -->
<section class="section">
<div class="container is-max-desktop">
<h2 class="defense-heading">When agents can repeatedly query prices, realizable <span class="underline">markup disappears</span>.</h2>
<div class="coi-equation">
<div class="formula">COI = <em>E</em>[P] &minus; <u>p</u></div>
<p class="caption">Cost of Information &mdash; the expected premium dynamic pricing earns over the reservation price &mdash; collapses to zero as the number of independent querying agents grows.</p>
</div>
</div>
</section>
<!-- End COI vulnerability -->
<section class="section defense-method-section">
<div class="container is-max-desktop">
<h2 class="defense-heading">We study behavior, convert it into a control signal, and train a pricing policy that survives contamination.</h2>
<div class="defense-method-grid">
<article class="defense-step">
<span class="step-num">01</span>
<h3>Observe</h3>
<p>Human participants and LLM agents complete goal-driven hotel and airline tasks. The storefront records behavior events and price quotes as timestamped trajectories.</p>
</article>
<article class="defense-step">
<span class="step-num">02</span>
<h3>Distinguish</h3>
<p>Session paths become transition kernels. KL distance to human and agent prototypes yields a continuous agent-probability signal.</p>
</article>
<article class="defense-step">
<span class="step-num">03</span>
<h3>Defend</h3>
<p>A contamination generator mixes human and synthetic agent trajectories. A distributionally robust RL policy optimizes price under worst-case demand shifts.</p>
</article>
</div>
</div>
</section>
<!-- Defense-styled: three takeaways and forward-deploy line -->
<section class="section defense-block">
<div class="container is-max-desktop">
<h2 class="defense-heading">Agents <span class="mark">distort marketplace signals</span>. PHANTOM uses behavioral distinguishability and DR&ndash;RL to <span class="mark">preserve pricing power</span>.</h2>
<ol class="takeaways">
<li>
<span class="num">01</span>
<span>We can <strong>distinguish humans from agents</strong> at the transition-kernel level.<span class="stat">Mann&ndash;Whitney U = 2.0, p = 0.0006 across 29 labeled sessions.</span></span>
</li>
<li>
<span class="num">02</span>
<span>Revenue <strong>declines monotonically</strong> in agent-contaminated systems.<span class="stat">Each 1.0 step of contamination &alpha; removes ~90,140 in cohort revenue (p &lt; 10<sup>-77</sup>).</span></span>
</li>
<li>
<span class="num">03</span>
<span>Distributionally robust RL <strong>preserves margin</strong> under worst-case contamination.<span class="stat">Defended policy holds positive COI gap over baseline across &alpha; &isin; [0, 1].</span></span>
</li>
</ol>
<p class="deploy-line">Our solution can be forward-deployed to any e-commerce platform to <strong>preserve their COI</strong>.</p>
<div class="hf-callout">
<div class="hf-emoji" aria-hidden="true">&#129303;</div>
<div>
<h4>WhoClickedIt &mdash; published on Hugging Face.</h4>
<p>~4k rows of labeled human and agent interaction data across hotel and airline tasks. Open dataset used for training the behavioral kernels.</p>
<a href="https://huggingface.co/datasets/velocitatem/whoclickedit" target="_blank" rel="noopener">huggingface.co/datasets/velocitatem/whoclickedit</a>
</div>
</div>
</div>
</section>
<!-- End takeaways -->
<!-- Image carousel -->
@@ -428,35 +499,7 @@
<!-- Video carousel -->
<section class="hero is-small">
<div class="hero-body">
<div class="container">
<h2 class="title is-3">Defense Scenes</h2>
<div id="videos-carousel" class="carousel results-carousel">
<div class="item item-video1">
<video poster="" id="video1" controls muted loop height="100%" preload="metadata">
<source src="static/videos/COIFirstPrinciplesScene.mp4" type="video/mp4">
</video>
<h2 class="subtitle has-text-centered">COI from first principles.</h2>
</div>
<div class="item item-video2">
<video poster="" id="video2" controls muted loop height="100%" preload="metadata">
<source src="static/videos/BehaviorKernelConstructionScene.mp4" type="video/mp4">
</video>
<h2 class="subtitle has-text-centered">Behavioral kernel construction: learning how humans and agents differ.</h2>
</div>
<div class="item item-video3">
<video poster="" id="video3" controls muted loop height="100%" preload="metadata">
<source src="static/videos/RobustControlScene.mp4" type="video/mp4">
</video>
<h2 class="subtitle has-text-centered">Distributionally robust control loop.</h2>
</div>
</div>
</div>
</div>
</section>
<!-- End video carousel -->
<!-- Defense Scenes video carousel removed -->
@@ -467,7 +510,7 @@
<section class="hero is-small is-light">
<div class="hero-body">
<div class="container">
<h2 class="title">Full Thesis</h2>
<h2 class="title is-3">Full thesis.</h2>
<iframe title="PHANTOM thesis PDF" src="https://pub-d5b94a3c29fd40c6b3881946e463fdb7.r2.dev/thesis-latest.pdf" width="100%" height="550">
</iframe>

740
docs/static/css/defense-theme.css vendored Normal file
View File

@@ -0,0 +1,740 @@
:root {
--phantom-bg: #eef3f7;
--phantom-paper: rgba(255, 255, 255, 0.78);
--phantom-paper-solid: #ffffff;
--phantom-ink: #1f2a38;
--phantom-muted: #59636e;
--phantom-faint: #dce5eb;
--phantom-line: rgba(31, 42, 56, 0.13);
--phantom-teal: #28aaa5;
--phantom-teal-dark: #16837f;
--phantom-blue: #527dad;
--phantom-blue-soft: rgba(82, 125, 173, 0.18);
--phantom-shadow: 0 28px 80px rgba(31, 42, 56, 0.12);
--phantom-soft-shadow: 0 14px 45px rgba(31, 42, 56, 0.08);
}
html {
scroll-behavior: smooth;
background: var(--phantom-bg);
}
body {
color: var(--phantom-ink);
background:
radial-gradient(70rem 22rem at 72% 8%, rgba(31, 42, 56, 0.15), transparent 58%),
radial-gradient(54rem 24rem at 20% 62%, rgba(31, 42, 56, 0.12), transparent 62%),
linear-gradient(180deg, #f7fafc 0%, var(--phantom-bg) 48%, #f8fafb 100%);
font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
letter-spacing: -0.02em;
}
body::before {
content: "";
position: fixed;
inset: -20vh -10vw auto -10vw;
height: 72vh;
pointer-events: none;
z-index: -1;
opacity: 0.88;
filter: blur(30px);
background:
radial-gradient(45rem 16rem at 9% 34%, rgba(18, 23, 31, 0.11), transparent 62%),
radial-gradient(35rem 11rem at 65% 24%, rgba(18, 23, 31, 0.13), transparent 65%),
radial-gradient(42rem 17rem at 45% 88%, rgba(82, 125, 173, 0.13), transparent 68%);
}
strong, b {
font-weight: 700;
color: inherit;
}
a {
color: var(--phantom-blue);
text-decoration-thickness: 0.08em;
text-underline-offset: 0.18em;
}
main {
overflow: hidden;
}
.container.is-max-desktop {
max-width: 1180px !important;
}
.section,
.hero-body {
position: relative;
}
.section {
padding: 5rem 1.5rem;
}
.title,
.subtitle,
h1,
h2,
h3,
h4,
button,
.button {
/* important needed to beat index.css serif heading rule */
font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace !important;
letter-spacing: -0.04em;
}
/* Defense cover */
.defense-cover {
min-height: 92vh;
padding: 2rem 0 3.5rem;
background:
linear-gradient(180deg, rgba(255, 255, 255, 0.68), rgba(238, 243, 247, 0.92));
}
.defense-cover::after {
content: "";
position: absolute;
right: -10vw;
bottom: -13rem;
width: 64vw;
height: 26rem;
background: rgba(31, 42, 56, 0.12);
filter: blur(38px);
border-radius: 65% 35% 47% 53%;
transform: rotate(-8deg);
pointer-events: none;
}
.defense-cover .hero-body {
padding: 5.5rem 1.5rem 4rem;
}
.defense-hero-grid {
display: grid;
grid-template-columns: minmax(0, 1.08fr) minmax(280px, 0.72fr);
gap: clamp(2rem, 6vw, 7rem);
align-items: center;
}
.defense-kicker,
.defense-meta-row,
.defense-chip-row,
.defense-mini-label,
.tpu-credit {
color: rgba(31, 42, 56, 0.66);
font-size: 0.88rem;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.defense-kicker {
margin-bottom: 1.2rem;
}
.publication-title.defense-title {
margin: 0;
color: var(--phantom-ink);
font-size: clamp(4.25rem, 12vw, 10rem);
line-height: 0.82;
font-weight: 800;
letter-spacing: -0.09em;
text-transform: uppercase;
}
.defense-subtitle {
max-width: 950px;
margin: clamp(1.5rem, 3vw, 2.3rem) 0 0;
color: var(--phantom-ink);
font-size: clamp(1.55rem, 3.2vw, 3.35rem);
line-height: 1.35;
font-weight: 400;
letter-spacing: 0.05em;
}
.mark,
mark,
.defense-highlight {
background: linear-gradient(0deg, var(--phantom-blue) 0%, var(--phantom-blue) 100%);
color: #ffffff;
padding: 0 0.1em;
line-height: inherit;
box-decoration-break: clone;
-webkit-box-decoration-break: clone;
}
.defense-chip-row {
display: flex;
flex-wrap: wrap;
gap: 0.7rem 0.9rem;
margin-top: 2.15rem;
text-transform: none;
letter-spacing: -0.02em;
font-size: clamp(0.92rem, 1.4vw, 1.2rem);
}
.defense-chip {
display: inline-flex;
align-items: center;
gap: 0.65rem;
color: rgba(31, 42, 56, 0.70);
}
.defense-chip::before {
content: "";
width: 0.42rem;
height: 0.42rem;
border-radius: 999px;
background: var(--phantom-blue);
box-shadow: 0 0 0 0.35rem var(--phantom-blue-soft);
}
.defense-meta-card {
margin-top: 2.4rem;
display: inline-flex;
flex-wrap: wrap;
gap: 0.6rem 1rem;
align-items: center;
padding: 0.85rem 1rem;
border: 1px solid var(--phantom-line);
border-radius: 999px;
background: rgba(255, 255, 255, 0.58);
backdrop-filter: blur(18px);
box-shadow: var(--phantom-soft-shadow);
color: rgba(31, 42, 56, 0.74);
font-size: 0.95rem;
}
.defense-meta-card .dot {
width: 0.26rem;
height: 0.26rem;
border-radius: 50%;
background: var(--phantom-teal);
}
.defense-links {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
margin-top: 1.8rem;
}
.defense-links .button,
.publication-links .button {
border: 1px solid rgba(31, 42, 56, 0.18) !important;
background: rgba(31, 42, 56, 0.92) !important;
color: #ffffff !important;
box-shadow: 0 12px 30px rgba(31, 42, 56, 0.16);
transition: transform 180ms ease, box-shadow 180ms ease, background 180ms ease;
}
.defense-links .button:hover,
.publication-links .button:hover {
transform: translateY(-2px);
box-shadow: 0 16px 40px rgba(31, 42, 56, 0.20);
background: var(--phantom-ink) !important;
}
.defense-links .button.is-light-outline {
background: rgba(255, 255, 255, 0.72) !important;
color: var(--phantom-ink) !important;
}
.tpu-credit {
margin-top: 1.35rem;
text-transform: none;
letter-spacing: 0.02em;
}
.tpu-credit .accent {
color: var(--phantom-blue);
font-weight: 700;
}
.defense-visual {
justify-self: end;
width: min(100%, 430px);
}
.defense-orbit-card {
position: relative;
min-height: 435px;
border: 1px solid var(--phantom-line);
border-radius: 2rem;
background: linear-gradient(145deg, rgba(255, 255, 255, 0.84), rgba(238, 243, 247, 0.68));
box-shadow: var(--phantom-shadow);
overflow: hidden;
}
.defense-orbit-card::before {
content: "";
position: absolute;
inset: 2rem;
border: 1px dashed rgba(31, 42, 56, 0.22);
border-radius: 44% 56% 58% 42%;
transform: rotate(-14deg);
}
.defense-orbit-card::after {
content: "";
position: absolute;
right: -4rem;
bottom: -5rem;
width: 18rem;
height: 12rem;
background: rgba(40, 170, 165, 0.14);
border-radius: 50%;
filter: blur(18px);
}
.defense-art-stack {
position: relative;
display: grid;
min-height: 435px;
place-items: center;
z-index: 1;
}
.defense-art-stack .agent-art {
width: min(70%, 250px);
transform: translateY(-12px);
filter: drop-shadow(0 28px 28px rgba(31, 42, 56, 0.12));
}
.defense-art-stack .mini-token {
position: absolute;
width: 4.8rem;
height: 4.8rem;
display: grid;
place-items: center;
border-radius: 1.3rem;
border: 1px solid rgba(82, 125, 173, 0.28);
background: rgba(255, 255, 255, 0.72);
color: var(--phantom-blue);
box-shadow: var(--phantom-soft-shadow);
font-size: 1.35rem;
}
.defense-art-stack .mini-token:nth-child(2) { top: 3.1rem; right: 3.2rem; }
.defense-art-stack .mini-token:nth-child(3) { left: 2.8rem; bottom: 6.1rem; color: var(--phantom-teal-dark); }
.defense-art-stack .mini-token:nth-child(4) { right: 5.6rem; bottom: 3.2rem; color: var(--phantom-ink); }
/* Defense overview strip */
.defense-overview-strip {
margin-top: -3.8rem;
padding: 0 1.5rem 4.6rem;
position: relative;
z-index: 2;
}
.defense-overview-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
.defense-overview-card,
.actor-card,
.defense-step,
.hf-callout,
.coi-equation,
pre,
.publication-banner {
border: 1px solid var(--phantom-line);
background: var(--phantom-paper);
box-shadow: var(--phantom-soft-shadow);
backdrop-filter: blur(18px);
}
.defense-overview-card {
min-height: 9rem;
padding: 1.3rem;
border-radius: 1.4rem;
}
.defense-overview-card .num {
color: var(--phantom-blue);
font-weight: 700;
font-size: 0.85rem;
}
.defense-overview-card h3 {
margin: 1rem 0 0.45rem;
color: var(--phantom-ink);
font-size: clamp(1.15rem, 2vw, 1.65rem);
line-height: 1.05;
}
.defense-overview-card p {
color: var(--phantom-muted);
font-size: 0.95rem;
line-height: 1.45;
}
/* Main sections */
.hero.teaser,
.hero.is-small,
.hero.is-small.is-light,
.hero.is-light,
.section.hero.is-light,
.defense-block {
background: transparent !important;
}
.publication-banner {
padding: 1rem;
border-radius: 1.5rem;
overflow: hidden;
}
.publication-banner img,
.actor-art img {
filter: drop-shadow(0 18px 22px rgba(31, 42, 56, 0.10));
}
.defense-heading,
.title.is-3,
.content h2.title {
color: var(--phantom-ink) !important;
font-size: clamp(2rem, 4.5vw, 4.8rem) !important;
/* enough leading that .mark backgrounds on wrapped lines don't overlap adjacent text */
line-height: 1.2 !important;
font-weight: 700 !important;
letter-spacing: -0.06em !important;
text-align: left !important;
margin-bottom: 2rem !important;
}
.title.is-4,
.content h3.title {
color: var(--phantom-ink) !important;
font-size: clamp(1.35rem, 2.2vw, 2rem) !important;
font-weight: 700 !important;
letter-spacing: -0.05em !important;
margin-top: 2rem !important;
}
.content {
color: var(--phantom-muted);
font-size: 1.02rem;
line-height: 1.68;
}
.content.has-text-justified,
.content.has-text-justified p {
text-align: left !important;
}
.content p + p {
margin-top: 1.05rem;
}
.defense-block,
.section.hero.is-light {
position: relative;
}
.defense-block::before,
.section.hero.is-light::before {
content: "";
position: absolute;
inset: 1rem 0 auto 0;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(31, 42, 56, 0.13), transparent);
}
.defense-block .defense-heading {
margin-bottom: 3.5rem !important;
}
.actor-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.1rem;
position: relative;
z-index: 1;
}
.actor-card {
display: flex;
flex-direction: column;
min-height: 25rem;
padding: 1.4rem;
border-radius: 1.5rem;
}
.actor-card h3 {
margin-top: 1.1rem;
color: var(--phantom-ink);
font-size: clamp(1.6rem, 3vw, 2.55rem);
line-height: 1;
font-weight: 700;
}
.actor-card p {
margin-top: 1rem;
color: var(--phantom-muted);
font-size: 1rem;
line-height: 1.45;
}
.actor-art {
min-height: 12rem;
display: grid;
place-items: center;
}
.actor-art img {
max-height: 10.5rem;
width: auto;
}
.actor-icon {
display: grid;
width: 8.75rem;
height: 8.75rem;
place-items: center;
border: 2px solid rgba(82, 125, 173, 0.55);
border-radius: 1.7rem;
background: linear-gradient(145deg, rgba(40, 170, 165, 0.20), rgba(255, 255, 255, 0.94));
color: var(--phantom-teal-dark);
font-size: 3.6rem;
transform: rotate(-6deg);
}
.underline {
text-decoration: underline;
text-decoration-thickness: 0.09em;
text-underline-offset: 0.13em;
}
.coi-equation {
border-radius: 1.7rem;
padding: clamp(1.6rem, 4vw, 3rem);
}
.coi-equation .formula {
color: #111111;
font-family: Georgia, "Times New Roman", serif;
font-size: clamp(3rem, 9vw, 7.6rem);
line-height: 1;
letter-spacing: -0.07em;
}
.coi-equation .caption {
max-width: 780px;
margin-top: 1.3rem;
color: var(--phantom-muted);
font-size: 1.05rem;
}
.defense-method-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
margin-top: 2rem;
}
.defense-step {
border-radius: 1.4rem;
padding: 1.35rem;
}
.defense-step .step-num {
display: inline-grid;
place-items: center;
width: 2.6rem;
height: 2.6rem;
margin-bottom: 1rem;
border: 1px solid rgba(40, 170, 165, 0.38);
border-radius: 50%;
color: var(--phantom-teal-dark);
background: rgba(40, 170, 165, 0.10);
font-weight: 700;
}
.defense-step h3 {
margin: 0 0 0.55rem !important;
font-size: 1.45rem !important;
}
.defense-step p {
color: var(--phantom-muted);
font-size: 0.95rem;
line-height: 1.5;
}
.takeaways {
list-style: none;
margin: 2rem 0 0 !important;
padding: 0;
display: grid;
gap: 0.9rem;
}
.takeaways li {
display: grid;
grid-template-columns: 5rem minmax(0, 1fr);
gap: 1rem;
align-items: start;
padding: 1.2rem 1.3rem;
border: 1px solid var(--phantom-line);
border-radius: 1.2rem;
background: rgba(255, 255, 255, 0.68);
}
.takeaways .num {
color: var(--phantom-blue);
font-size: 1.4rem;
font-weight: 700;
}
.takeaways .stat {
display: block;
margin-top: 0.45rem;
color: var(--phantom-muted);
font-size: 0.9rem;
}
.deploy-line {
margin: 2rem 0 0;
color: var(--phantom-ink);
font-size: clamp(1.45rem, 3vw, 2.5rem);
line-height: 1.25;
font-weight: 700;
}
.deploy-line strong {
background: var(--phantom-blue);
color: #ffffff;
padding: 0 0.1em;
box-decoration-break: clone;
-webkit-box-decoration-break: clone;
}
.hf-callout {
display: grid;
grid-template-columns: auto 1fr;
gap: 1rem;
margin-top: 1.6rem;
padding: 1.2rem;
border-radius: 1.3rem;
}
.hf-emoji {
width: 3.6rem;
height: 3.6rem;
display: grid;
place-items: center;
border-radius: 1rem;
background: rgba(255, 215, 0, 0.20);
font-size: 1.9rem;
}
.hf-callout h4 {
margin: 0 0 0.3rem;
color: var(--phantom-ink);
font-size: 1.12rem;
}
.hf-callout p {
margin: 0 0 0.35rem;
color: var(--phantom-muted);
}
pre#bibtex-code,
pre {
border-radius: 1.2rem;
color: var(--phantom-ink);
}
.footer {
background: rgba(255, 255, 255, 0.54);
border-top: 1px solid var(--phantom-line);
color: var(--phantom-muted);
}
.more-works-container,
.scroll-to-top {
font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
}
.more-works-btn,
.scroll-to-top {
background: rgba(255, 255, 255, 0.78) !important;
color: var(--phantom-ink) !important;
border: 1px solid var(--phantom-line) !important;
box-shadow: var(--phantom-soft-shadow) !important;
backdrop-filter: blur(16px);
}
.more-works-dropdown {
border: 1px solid var(--phantom-line) !important;
border-radius: 1.2rem !important;
box-shadow: var(--phantom-shadow) !important;
}
@media (max-width: 900px) {
.defense-hero-grid,
.defense-overview-grid,
.actor-grid,
.defense-method-grid {
grid-template-columns: 1fr;
}
.defense-cover .hero-body {
padding-top: 4rem;
}
.defense-visual {
justify-self: stretch;
}
.defense-orbit-card,
.defense-art-stack {
min-height: 330px;
}
.defense-art-stack .agent-art {
width: min(56%, 205px);
}
.defense-meta-card {
border-radius: 1.2rem;
}
.publication-title.defense-title {
font-size: clamp(4rem, 18vw, 6.2rem);
}
}
@media (max-width: 560px) {
.section {
padding: 3.6rem 1.1rem;
}
.defense-cover .hero-body {
padding-left: 1.1rem;
padding-right: 1.1rem;
}
.defense-subtitle {
letter-spacing: 0.02em;
}
.takeaways li,
.hf-callout {
grid-template-columns: 1fr;
}
.defense-chip-row,
.defense-links {
flex-direction: column;
align-items: stretch;
}
.defense-links .button {
justify-content: center;
}
}

View File

@@ -31,6 +31,15 @@
--paper-ink-strong: #303030;
--paper-ink-soft: #6b6b6b;
--paper-accent: #2f2f2f;
--defense-bg: #f4f7f9;
--defense-surface: #ffffff;
--defense-teal: #5fb3a8;
--defense-teal-soft: #b9e2dc;
--defense-teal-bg: #c8ebe5;
--defense-blue: #2d6f9f;
--defense-ink: #1d2630;
--defense-ink-soft: #5a6470;
--mono-stack: "IBM Plex Mono", "JetBrains Mono", "Roboto Mono", "SF Mono", Menlo, Consolas, monospace;
--radius: 10px;
--radius-lg: 14px;
}

123
docs/static/images/agent.svg vendored Normal file
View File

@@ -0,0 +1,123 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" width="100%" height="100%">
<defs>
<radialGradient id="bgShadow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#143547" stop-opacity="0.15" />
<stop offset="100%" stop-color="#ffffff" stop-opacity="0" />
</radialGradient>
<radialGradient id="innerShadow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#15334c" stop-opacity="0.35" />
<stop offset="100%" stop-color="#ffffff" stop-opacity="0" />
</radialGradient>
<linearGradient id="cubeTop" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#ffffff" />
<stop offset="100%" stop-color="#e2f1f7" />
</linearGradient>
<linearGradient id="cubeLeft" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#5caec4" />
<stop offset="100%" stop-color="#337899" />
</linearGradient>
<linearGradient id="cubeRight" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#9ce6d7" />
<stop offset="100%" stop-color="#5fbfa9" />
</linearGradient>
<linearGradient id="headTop" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#ffffff" />
<stop offset="100%" stop-color="#eef6fa" />
</linearGradient>
<linearGradient id="headLeft" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#ffffff" />
<stop offset="100%" stop-color="#d9eaf0" />
</linearGradient>
<linearGradient id="headRight" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#ffffff" />
<stop offset="100%" stop-color="#e3eff5" />
</linearGradient>
<linearGradient id="screenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#0c2340" />
<stop offset="100%" stop-color="#16355c" />
</linearGradient>
<linearGradient id="recessGrad" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#bcdde8" />
<stop offset="100%" stop-color="#edf6f9" />
</linearGradient>
<style>
.stroke-main { stroke: #1a446c; stroke-width: 7px; stroke-linecap: round; stroke-linejoin: round; }
.stroke-medium { stroke: #1a446c; stroke-width: 5.5px; stroke-linecap: round; stroke-linejoin: round; }
.stroke-thin { stroke: #1a446c; stroke-width: 4px; stroke-linecap: round; stroke-linejoin: round; }
.circuit-line { fill: none; stroke: #ffffff; stroke-width: 4px; stroke-linecap: round; stroke-linejoin: round; stroke-opacity: 0.9; }
.circuit-node { fill: #ffffff; stroke: #1a446c; stroke-width: 3.5px; }
.glow-eye { fill: #6bf2ff; filter: drop-shadow(0px 0px 3px rgba(107,242,255,0.8)); }
</style>
</defs>
<ellipse cx="400" cy="700" rx="220" ry="75" fill="url(#bgShadow)" />
<polygon points="220,415 400,519 400,710 220,606" fill="url(#cubeLeft)" />
<polygon points="400,519 580,415 580,606 400,710" fill="url(#cubeRight)" />
<polygon points="220,415 400,519 400,450 220,346" fill="#ffffff" fill-opacity="0.35" />
<polygon points="400,519 580,415 580,346 400,450" fill="#ffffff" fill-opacity="0.15" />
<polygon points="220,346 400,242 580,346 400,450" fill="url(#cubeTop)" />
<polygon points="220,346 400,242 580,346 580,606 400,710 220,606" fill="none" class="stroke-main" />
<line x1="400" y1="450" x2="400" y2="710" class="stroke-main" />
<line x1="220" y1="415" x2="400" y2="519" class="stroke-main" />
<line x1="400" y1="519" x2="580" y2="415" class="stroke-main" />
<path d="M 260,510 L 315,542 L 315,615" class="circuit-line" />
<circle cx="260" cy="510" r="6" class="circuit-node" />
<path d="M 285,575 L 340,607 L 340,670" class="circuit-line" />
<circle cx="285" cy="575" r="6" class="circuit-node" />
<path d="M 365,550 L 365,685" class="circuit-line" />
<circle cx="365" cy="550" r="6" class="circuit-node" />
<path d="M 540,510 L 460,556 L 460,640" class="circuit-line" />
<circle cx="540" cy="510" r="6" class="circuit-node" />
<path d="M 520,570 L 485,590 L 485,665" class="circuit-line" />
<circle cx="520" cy="570" r="6" class="circuit-node" />
<polygon points="275,346 400,274 525,346 400,418" fill="url(#recessGrad)" class="stroke-thin" />
<ellipse cx="400" cy="346" rx="65" ry="28" fill="url(#innerShadow)" />
<polygon points="340,335 400,300 460,335 400,370" fill="#142d42" fill-opacity="0.25" />
<polygon points="310,250 400,302 400,397 310,345" fill="url(#headLeft)" />
<polygon points="400,302 490,250 490,345 400,397" fill="url(#headRight)" />
<polygon points="310,250 400,198 490,250 400,302" fill="url(#headTop)" />
<polygon points="310,250 400,198 490,250 490,345 400,397 310,345" fill="none" class="stroke-medium" />
<line x1="400" y1="302" x2="400" y2="397" class="stroke-medium" />
<polygon points="325,268 385,303 385,370 325,335" fill="url(#screenGrad)" class="stroke-thin" />
<line x1="343" y1="306" x2="343" y2="322" stroke="#6bf2ff" stroke-width="7.5" stroke-linecap="round" class="glow-eye" />
<line x1="367" y1="320" x2="367" y2="336" stroke="#6bf2ff" stroke-width="7.5" stroke-linecap="round" class="glow-eye" />
<ellipse cx="400" cy="230" rx="12" ry="6" fill="#325b7f" />
<path d="M 320,165 C 320,142 480,142 480,165" fill="none" stroke="#63cbd1" stroke-width="6" stroke-linecap="round" stroke-opacity="0.8" />
<line x1="400" y1="230" x2="400" y2="135" class="stroke-medium" />
<path d="M 480,165 C 480,188 320,188 320,165" fill="none" stroke="#63cbd1" stroke-width="6" stroke-linecap="round" />
<circle cx="400" cy="135" r="10" fill="#5cbfa9" class="stroke-medium" />
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
docs/static/images/apple-touch-icon.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -56,7 +56,7 @@
<g transform="translate(60, 580)">
<text x="0" y="30" font-size="24" font-weight="bold" fill="#444">Why COI Erodes with Agent Saturation</text>
<text x="0" y="75" font-family="Georgia, serif" font-style="italic" font-size="22" fill="#8C7A6B">p<tspan font-size="14" dy="5">(1)</tspan><tspan dy="-5"> = min(p</tspan><tspan font-size="14" dy="5">1</tspan><tspan dy="-5">, ..., p</tspan><tspan font-size="14" dy="5">N</tspan><tspan dy="-5">)</tspan></text>
<text x="0" y="115" font-family="Georgia, serif" font-style="italic" font-size="22" fill="#8C7A6B">P(p<tspan font-size="14" dy="5">(1)</tspan><tspan dy="-5"> > t) = [1 - F(t)]</tspan><tspan font-size="14" dy="-10">N</tspan></text>
<text x="0" y="115" font-family="Georgia, serif" font-style="italic" font-size="22" fill="#8C7A6B">P(p<tspan font-size="14" dy="5">(1)</tspan><tspan dy="-5"> &gt; t) = [1 - F(t)]</tspan><tspan font-size="14" dy="-10">N</tspan></text>
<!-- Erosion Graph -->
<rect x="120" y="150" width="280" height="230" fill="#FFFFFF" filter="url(#shadow)" rx="8"/>
@@ -129,9 +129,9 @@
<text x="250" y="440" font-size="18" fill="#777" text-anchor="middle">Kernel shape is the compact behavioral signature used downstream.</text>
</g>
<!-- Bottom: Separability Distributions -->
<!-- Bottom: Distinguishability Distributions -->
<g transform="translate(700, 600)">
<text x="0" y="30" font-size="24" font-weight="bold" fill="#444">Separability into a Control Signal</text>
<text x="0" y="30" font-size="24" font-weight="bold" fill="#444">Distinguishability into a Control Signal</text>
<text x="0" y="75" font-family="Georgia, serif" font-style="italic" font-size="22" fill="#8C7A6B">Δ<tspan font-size="16" dy="5">H</tspan><tspan dy="-5"> = D</tspan><tspan font-size="16" dy="5">KL</tspan><tspan dy="-5">(T&#770;' || T&#772;</tspan><tspan font-size="16" dy="5">H</tspan><tspan dy="-5">)</tspan></text>
<text x="0" y="115" font-family="Georgia, serif" font-style="italic" font-size="22" fill="#8C7A6B">Δ<tspan font-size="16" dy="5">A</tspan><tspan dy="-5"> = D</tspan><tspan font-size="16" dy="5">KL</tspan><tspan dy="-5">(T&#770;' || T&#772;</tspan><tspan font-size="16" dy="5">A</tspan><tspan dy="-5">)</tspan></text>
<text x="0" y="155" font-family="Georgia, serif" font-style="italic" font-size="24" fill="#8C7A6B">g = Δ<tspan font-size="16" dy="5">H</tspan><tspan dy="-5"> - Δ</tspan><tspan font-size="16" dy="5">A</tspan></text>

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 15 KiB

87
docs/static/images/human.svg vendored Normal file
View File

@@ -0,0 +1,87 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" width="100%" height="100%">
<defs>
<radialGradient id="dropShadow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#143547" stop-opacity="0.15" />
<stop offset="100%" stop-color="#ffffff" stop-opacity="0" />
</radialGradient>
<radialGradient id="sphereShadow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#1c4561" stop-opacity="0.4" />
<stop offset="60%" stop-color="#2a5c7c" stop-opacity="0.15" />
<stop offset="100%" stop-color="#ffffff" stop-opacity="0" />
</radialGradient>
<linearGradient id="cubeTop" x1="25%" y1="0%" x2="75%" y2="100%">
<stop offset="0%" stop-color="#ffffff" />
<stop offset="100%" stop-color="#e1eff7" />
</linearGradient>
<linearGradient id="cubeLeft" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#5faec4" />
<stop offset="100%" stop-color="#367fa3" />
</linearGradient>
<linearGradient id="cubeRight" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#9ce6d7" />
<stop offset="100%" stop-color="#67c7b3" />
</linearGradient>
<linearGradient id="rimLeft" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#ffffff" />
<stop offset="100%" stop-color="#cbdfe8" />
</linearGradient>
<linearGradient id="rimRight" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#ffffff" />
<stop offset="100%" stop-color="#d5e6ed" />
</linearGradient>
<linearGradient id="sphereGrad" x1="30%" y1="10%" x2="70%" y2="90%">
<stop offset="0%" stop-color="#ffffff" />
<stop offset="15%" stop-color="#fffdf3" />
<stop offset="65%" stop-color="#fbf2d5" />
<stop offset="100%" stop-color="#eadebe" />
</linearGradient>
<style>
.outline {
stroke: #1e517b;
stroke-width: 8px;
stroke-linecap: round;
stroke-linejoin: round;
}
.inner-line {
stroke: #1e517b;
stroke-width: 6px;
stroke-linecap: round;
stroke-linejoin: round;
}
</style>
</defs>
<ellipse cx="430" cy="710" rx="180" ry="60" transform="rotate(-15, 430, 710)" fill="url(#dropShadow)" />
<polygon points="310,470 500,570 500,750 310,650" fill="url(#cubeLeft)" />
<polygon points="500,570 690,470 690,650 500,750" fill="url(#cubeRight)" />
<polygon points="310,400 500,500 500,570 310,470" fill="url(#rimLeft)" />
<polygon points="500,500 690,400 690,470 500,570" fill="url(#rimRight)" />
<polygon points="310,400 500,300 690,400 500,500" fill="url(#cubeTop)" />
<ellipse cx="500" cy="370" rx="75" ry="35" fill="url(#sphereShadow)" />
<polygon points="310,400 500,300 690,400 690,650 500,750 310,650" fill="none" class="outline" />
<line x1="310" y1="470" x2="500" y2="570" class="inner-line" />
<line x1="500" y1="570" x2="690" y2="470" class="inner-line" />
<line x1="500" y1="500" x2="500" y2="750" class="inner-line" />
<line x1="310" y1="400" x2="500" y2="500" class="inner-line" />
<line x1="500" y1="500" x2="690" y2="400" class="inner-line" />
<g id="sphere">
<circle cx="500" cy="266" r="90" fill="url(#sphereGrad)" class="outline" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -0,0 +1,51 @@
---
present_time: 15 minutes
qa: 15 minutes
---
> Notes for presentation deck: keep minimal text, highlight only key metrics or keywords and diagrams, if possible do progressive reveal of items on slides, if going through a list, make each appear progressively on new slides like an animation.
# Introduction [2min]
> Hook: Extracting margin in markets with high density of AI agents.
- Say what today's agenda is (show in the blocks at the botton of each slide and with each slide indicate which stage we are at)
- Highlight problem (add financial consequence)
- What are we trying to answer?
# First Stage (Platform Development) [4min]
- Talk about designing the platform (nextjs design and apache airflow and kafka)
## About the Platform
- Show an architecture diagram.
## Dataset Brief
- Screenshot of the HF dataset and highlight some key features of the dataset with big numbers indicated.
## Experimental Design
- Say how we collected data and how we used AI Agents
### AI Agents
- browser use
- models used (say we used the LLM router for different models)
# Second Stage (Distinguishability Construction) [4min]
- Explain kernels of behavior (what is a kernel)
- How we separate kernels and finally how we turn that into a probability.
# DR-RL [4min]
- Explain simple wesserstein balls and ambiguity
- Highlight computational complexity
## Results [1min]
- Empirical results from experiments
# Conclusions
- Consequences of our work (financial and future implications for pricing systems)
- Did we answer what we wanted? How?
# Appendix
## Derivation of the COI theorem
## Reward Structure Composition
## On our Sample Size

137
paper/defense/defense.nav Normal file
View File

@@ -0,0 +1,137 @@
\headcommand {\slideentry {0}{0}{1}{1/1}{}{0}}
\headcommand {\beamer@framepages {1}{1}}
\headcommand {\slideentry {0}{0}{2}{2/2}{}{0}}
\headcommand {\beamer@framepages {2}{2}}
\headcommand {\slideentry {0}{0}{3}{3/3}{}{0}}
\headcommand {\beamer@framepages {3}{3}}
\headcommand {\slideentry {0}{0}{4}{4/4}{}{0}}
\headcommand {\beamer@framepages {4}{4}}
\headcommand {\slideentry {0}{0}{5}{5/5}{}{0}}
\headcommand {\beamer@framepages {5}{5}}
\headcommand {\slideentry {0}{0}{6}{6/6}{}{0}}
\headcommand {\beamer@framepages {6}{6}}
\headcommand {\slideentry {0}{0}{7}{7/7}{}{0}}
\headcommand {\beamer@framepages {7}{7}}
\headcommand {\slideentry {0}{0}{8}{8/8}{}{0}}
\headcommand {\beamer@framepages {8}{8}}
\headcommand {\slideentry {0}{0}{9}{9/9}{}{0}}
\headcommand {\beamer@framepages {9}{9}}
\headcommand {\slideentry {0}{0}{10}{10/10}{}{0}}
\headcommand {\beamer@framepages {10}{10}}
\headcommand {\beamer@sectionpages {1}{10}}
\headcommand {\beamer@subsectionpages {1}{10}}
\headcommand {\sectionentry {1}{Platform Development}{11}{Platform Development}{0}}
\headcommand {\slideentry {1}{0}{1}{11/11}{}{0}}
\headcommand {\beamer@framepages {11}{11}}
\headcommand {\slideentry {1}{0}{2}{12/12}{}{0}}
\headcommand {\beamer@framepages {12}{12}}
\headcommand {\slideentry {1}{0}{3}{13/13}{}{0}}
\headcommand {\beamer@framepages {13}{13}}
\headcommand {\beamer@sectionpages {11}{13}}
\headcommand {\beamer@subsectionpages {11}{13}}
\headcommand {\sectionentry {2}{Distinguishability Construction}{14}{Distinguishability Construction}{0}}
\headcommand {\slideentry {2}{0}{1}{14/14}{}{0}}
\headcommand {\beamer@framepages {14}{14}}
\headcommand {\slideentry {2}{0}{2}{15/15}{}{0}}
\headcommand {\beamer@framepages {15}{15}}
\headcommand {\slideentry {2}{0}{3}{16/16}{}{0}}
\headcommand {\beamer@framepages {16}{16}}
\headcommand {\beamer@sectionpages {14}{16}}
\headcommand {\beamer@subsectionpages {14}{16}}
\headcommand {\sectionentry {3}{Distributionally Robust RL}{17}{Distributionally Robust RL}{0}}
\headcommand {\slideentry {3}{0}{1}{17/17}{}{0}}
\headcommand {\beamer@framepages {17}{17}}
\headcommand {\slideentry {3}{0}{2}{18/18}{}{0}}
\headcommand {\beamer@framepages {18}{18}}
\headcommand {\slideentry {3}{0}{3}{19/19}{}{0}}
\headcommand {\beamer@framepages {19}{19}}
\headcommand {\beamer@sectionpages {17}{19}}
\headcommand {\beamer@subsectionpages {17}{19}}
\headcommand {\sectionentry {4}{Results}{20}{Results}{0}}
\headcommand {\slideentry {4}{0}{1}{20/20}{}{0}}
\headcommand {\beamer@framepages {20}{20}}
\headcommand {\beamer@sectionpages {20}{20}}
\headcommand {\beamer@subsectionpages {20}{20}}
\headcommand {\sectionentry {5}{Conclusions}{21}{Conclusions}{0}}
\headcommand {\slideentry {5}{0}{1}{21/21}{}{0}}
\headcommand {\beamer@framepages {21}{21}}
\headcommand {\slideentry {5}{0}{2}{22/22}{}{0}}
\headcommand {\beamer@framepages {22}{22}}
\headcommand {\slideentry {5}{0}{3}{23/23}{}{0}}
\headcommand {\beamer@framepages {23}{23}}
\headcommand {\gdef \insertmainframenumber {23}}
\headcommand {\partentry {\translate {Appendix}}{1}}
\headcommand {\beamer@partpages {1}{23}}
\headcommand {\beamer@sectionpages {21}{23}}
\headcommand {\beamer@subsectionpages {21}{23}}
\headcommand {\beamer@appendixpages {24}}
\headcommand {\beamer@sectionpages {24}{23}}
\headcommand {\beamer@subsectionpages {24}{23}}
\headcommand {\sectionentry {6}{Appendix}{24}{Appendix}{1}}
\headcommand {\slideentry {6}{0}{1}{24/24}{}{1}}
\headcommand {\beamer@framepages {24}{24}}
\headcommand {\slideentry {6}{0}{2}{25/25}{}{1}}
\headcommand {\beamer@framepages {25}{25}}
\headcommand {\slideentry {6}{0}{3}{26/26}{}{1}}
\headcommand {\beamer@framepages {26}{26}}
\headcommand {\slideentry {6}{0}{4}{27/27}{}{1}}
\headcommand {\beamer@framepages {27}{27}}
\headcommand {\slideentry {6}{0}{5}{28/28}{}{1}}
\headcommand {\beamer@framepages {28}{28}}
\headcommand {\slideentry {6}{0}{6}{29/29}{}{1}}
\headcommand {\beamer@framepages {29}{29}}
\headcommand {\slideentry {6}{0}{7}{30/30}{}{1}}
\headcommand {\beamer@framepages {30}{30}}
\headcommand {\slideentry {6}{0}{8}{31/31}{}{1}}
\headcommand {\beamer@framepages {31}{31}}
\headcommand {\slideentry {6}{0}{9}{32/32}{}{1}}
\headcommand {\beamer@framepages {32}{32}}
\headcommand {\slideentry {6}{0}{10}{33/33}{}{1}}
\headcommand {\beamer@framepages {33}{33}}
\headcommand {\slideentry {6}{0}{11}{34/34}{}{1}}
\headcommand {\beamer@framepages {34}{34}}
\headcommand {\slideentry {6}{0}{12}{35/35}{}{1}}
\headcommand {\beamer@framepages {35}{35}}
\headcommand {\slideentry {6}{0}{13}{36/36}{}{1}}
\headcommand {\beamer@framepages {36}{36}}
\headcommand {\slideentry {6}{0}{14}{37/37}{}{1}}
\headcommand {\beamer@framepages {37}{37}}
\headcommand {\slideentry {6}{0}{15}{38/38}{}{1}}
\headcommand {\beamer@framepages {38}{38}}
\headcommand {\slideentry {6}{0}{16}{39/39}{}{1}}
\headcommand {\beamer@framepages {39}{39}}
\headcommand {\slideentry {6}{0}{17}{40/40}{}{1}}
\headcommand {\beamer@framepages {40}{40}}
\headcommand {\slideentry {6}{0}{18}{41/41}{}{1}}
\headcommand {\beamer@framepages {41}{41}}
\headcommand {\slideentry {6}{0}{19}{42/42}{}{1}}
\headcommand {\beamer@framepages {42}{42}}
\headcommand {\slideentry {6}{0}{20}{43/43}{}{1}}
\headcommand {\beamer@framepages {43}{43}}
\headcommand {\slideentry {6}{0}{21}{44/44}{}{1}}
\headcommand {\beamer@framepages {44}{44}}
\headcommand {\slideentry {6}{0}{22}{45/45}{}{1}}
\headcommand {\beamer@framepages {45}{45}}
\headcommand {\slideentry {6}{0}{23}{46/46}{}{1}}
\headcommand {\beamer@framepages {46}{46}}
\headcommand {\slideentry {6}{0}{24}{47/47}{}{1}}
\headcommand {\beamer@framepages {47}{47}}
\headcommand {\slideentry {6}{0}{25}{48/48}{}{1}}
\headcommand {\beamer@framepages {48}{48}}
\headcommand {\slideentry {6}{0}{26}{49/49}{}{1}}
\headcommand {\beamer@framepages {49}{49}}
\headcommand {\slideentry {6}{0}{27}{50/50}{}{1}}
\headcommand {\beamer@framepages {50}{50}}
\headcommand {\slideentry {6}{0}{28}{51/51}{}{1}}
\headcommand {\beamer@framepages {51}{51}}
\headcommand {\slideentry {6}{0}{29}{52/52}{}{1}}
\headcommand {\beamer@framepages {52}{52}}
\headcommand {\slideentry {6}{0}{30}{53/53}{}{1}}
\headcommand {\beamer@framepages {53}{53}}
\headcommand {\beamer@partpages {24}{53}}
\headcommand {\beamer@subsectionpages {24}{53}}
\headcommand {\beamer@sectionpages {24}{53}}
\headcommand {\beamer@documentpages {53}}
\headcommand {\gdef \inserttotalframenumber {30}}
\headcommand {\gdef \inserttotalframenumber {23}}
\headcommand {\gdef \appendixtotalframenumber {30}}

BIN
paper/defense/defense.pdf Normal file

Binary file not shown.

View File

932
paper/defense/defense.tex Normal file
View File

@@ -0,0 +1,932 @@
% Final thesis defense (PHANTOM)
% Build: cd paper/defense && pdflatex defense.tex && pdflatex defense.tex
%
% =========================================================================
% NOTATION GUIDE (every variable used in this deck is defined once here)
% =========================================================================
%
% PRICING & POLICY
% p_t price action at time t [EUR per booking]
% p_floor minimum viable price (marginal cost) [EUR per booking]
% E[P] expected price under policy pi [EUR per booking]
% pi(x_t) policy mapping context to price
% x_t context vector (product, time, behavior signals)
% R(p, q) revenue per session, equals p * q [EUR per session]
%
% COST OF INFORMATION
% COI(pi) = E[P] - p_floor [EUR per transaction]
% This is the average premium the platform extracts above marginal cost,
% i.e. the financial value of "knowing the customer's interest".
%
% BEHAVIOR & SESSIONS
% tau full session trajectory of (action, item, time) tuples
% tau' partial trajectory observed at scoring time
% T_hat(s'|s) empirical session transition kernel (a square table:
% rows are current actions, columns are next actions,
% each row sums to one)
% T_H_bar human prototype kernel (reference for human cohort)
% T_A_bar agent prototype kernel (reference for agent cohort)
%
% DETECTION SIGNAL
% Delta_H = KL(T_hat' || T_H_bar) distance to human prototype
% Delta_A = KL(T_hat' || T_A_bar) distance to agent prototype
% g(tau') = Delta_H - Delta_A signed gap, zero is the boundary
% f(tau') = sigmoid(g/T) in [0,1] agent-likelihood score
% T temperature for the sigmoid (unitless)
% threshold decision boundary at f = 0.5 (g = 0)
%
% DEMAND & CONTAMINATION
% d(p|theta) individual demand response, theta is type
% alpha contamination ratio, fraction of agent-mediated traffic
% Q(p|alpha) aggregate demand under contamination alpha
%
% ROBUST CONTROL
% P_hat_N empirical demand distribution from N samples
% U_eps(P_hat_N) Wasserstein ambiguity ball of radius eps
% lambda weight on COI-leakage penalty in the reward
% eta_ux weight on UX penalty in the reward
% c_info per-query info cost surrogate [EUR per query]
% UX(tau, p) user-experience penalty in [0,1]
%
% =========================================================================
\documentclass[aspectratio=169,11pt]{beamer}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{microtype}
\usepackage{amsmath,amssymb}
\usepackage{graphicx}
\usepackage{xspace}
\usepackage{booktabs}
\usepackage{appendixnumberbeamer}
\usepackage{hyperref}
\usepackage{tikz}
\usetikzlibrary{arrows.meta,calc,positioning,fit,shapes.geometric,shapes.misc,decorations.pathreplacing}
\graphicspath{{../src/chapters/figures/results/generated/final/plots/}{../src/chapters/}}
\usetheme[
progressbar=frametitle,
]{moloch}
\molochset{sectionpage=none,subsectionpage=none}
\usefonttheme{professionalfonts}
\setbeamertemplate{frame numbering}[fraction]
% Dark palette: deep ink canvas, cream paper text, orange + teal accents
\definecolor{PhantomPaper}{HTML}{F6F1E9}
\definecolor{PhantomInk}{HTML}{0F1B2D}
\definecolor{PhantomSlate}{HTML}{B5C2D2}
\definecolor{PhantomCyan}{HTML}{E08A4C}
\definecolor{PhantomIndigo}{HTML}{4FB8B2}
\definecolor{PhantomPeach}{HTML}{EEC39C}
\definecolor{PhantomMute}{HTML}{4A5566}
\setbeamercolor{normal text}{fg=PhantomPaper,bg=PhantomInk}
\setbeamercolor{background canvas}{bg=PhantomInk}
\setbeamercolor{alerted text}{fg=PhantomCyan}
\setbeamercolor{example text}{fg=PhantomIndigo}
\setbeamercolor{palette primary}{fg=PhantomPaper,bg=PhantomInk}
\setbeamercolor{frametitle}{fg=PhantomPaper,bg=PhantomInk}
\setbeamercolor{progress bar}{fg=PhantomCyan,bg=PhantomMute!50}
\setbeamercolor{title separator}{fg=PhantomCyan,bg=PhantomMute!50}
\setbeamercolor{structure}{fg=PhantomCyan}
\setbeamercolor{block title}{fg=PhantomPaper,bg=PhantomMute!70!PhantomInk}
\setbeamercolor{block body}{fg=PhantomPaper,bg=PhantomMute!35!PhantomInk}
\setbeamercolor{alertblock title}{fg=PhantomInk,bg=PhantomCyan}
\setbeamercolor{alertblock body}{fg=PhantomPaper,bg=PhantomCyan!25!PhantomInk}
\setbeamercolor{exampleblock title}{fg=PhantomInk,bg=PhantomIndigo}
\setbeamercolor{exampleblock body}{fg=PhantomPaper,bg=PhantomIndigo!25!PhantomInk}
\setbeamertemplate{navigation symbols}{}
\setbeamertemplate{itemize item}{\small\raise0.3ex\hbox{$\bullet$}}
\setbeamertemplate{itemize subitem}{\tiny\raise0.2ex\hbox{$\circ$}}
\hypersetup{colorlinks=true,urlcolor=PhantomCyan,linkcolor=PhantomInk}
\title{PHANTOM}
\subtitle{Pricing Heuristics Against Non-human Transaction Orchestration Mechanisms}
\author{Daniel R\"osel}
\institute{IE University, Madrid \\ Supervisor: Alberto Mart\'in Izquierdo}
\date{\today}
% Variable explanation block: \vardef{symbol}{description}
\newcommand{\vardef}[2]{%
\par\noindent\makebox[1.55cm][l]{\textbf{\color{PhantomCyan}#1}}%
\parbox[t]{\dimexpr\linewidth-1.65cm}{\small #2}\par\smallskip%
}
\newcommand{\metriccard}[2]{%
\begin{tikzpicture}
\node[
draw=PhantomInk,
rounded corners=3pt,
fill=PhantomCyan!10,
minimum width=3.05cm,
minimum height=1.25cm,
align=center
] {\Large\bfseries #1\\[-0.2em]{\scriptsize #2}};
\end{tikzpicture}%
}
% Compact inline icons (text-based, no nested tikzpictures).
% Dynamic color: uses current text color so icons stay visible inside
% blocks (dark text) and on the dark canvas (light text).
\newcommand{\humanicon}{\textbf{\textsf{H}}\xspace}
\newcommand{\roboticon}{\textcolor{PhantomCyan}{\textbf{\textsf{A}}}\xspace}
\newcommand{\platformicon}{\textcolor{PhantomIndigo}{\textbf{\textsf{P}}}\xspace}
% Big diagram icons (drawn directly inside an outer tikzpicture, no nesting)
\newcommand{\bighumanicon}[1]{%
\fill[PhantomInk] (#1) ++(0,0.45) circle (0.18);
\draw[PhantomInk,line width=0.6pt,fill=PhantomSlate!30] (#1) ellipse (0.32 and 0.22);
}
\newcommand{\bigroboticon}[1]{%
\draw[PhantomInk,line width=0.6pt,rounded corners=2pt,fill=PhantomCyan!30] ($(#1)+(-0.42,-0.30)$) rectangle ($(#1)+(0.42,0.34)$);
\fill[PhantomPaper] ($(#1)+(-0.18,0.05)$) circle (0.09);
\fill[PhantomPaper] ($(#1)+(0.18,0.05)$) circle (0.09);
\fill[PhantomInk] ($(#1)+(-0.18,0.05)$) circle (0.035);
\fill[PhantomInk] ($(#1)+(0.18,0.05)$) circle (0.035);
\draw[PhantomInk,line width=0.6pt] ($(#1)+(0,0.34)$) -- ($(#1)+(0,0.50)$);
\fill[PhantomCyan] ($(#1)+(0,0.55)$) circle (0.05);
}
\newcommand{\bigplatformicon}[1]{%
\draw[PhantomInk,line width=0.6pt,rounded corners=2pt,fill=PhantomIndigo!25] ($(#1)+(-0.32,-0.32)$) rectangle ($(#1)+(0.32,0.32)$);
\node[font=\small\bfseries,text=PhantomInk] at (#1) {P};
}
\tikzset{every node/.append style={text=PhantomInk}}
\begin{document}
% =========================================================================
% TITLE
% =========================================================================
\begin{frame}[plain]
\vfill
\centering
{\color{PhantomPaper}\Huge\bfseries PHANTOM\par}
\vspace{0.6em}
{\color{PhantomCyan}\rule{0.45\paperwidth}{0.06cm}\par}
\vspace{0.8em}
{\large\color{PhantomPaper}Pricing heuristics against non-human transaction orchestration\par}
\vfill
{\color{PhantomPaper}\normalsize Daniel R\"osel\par}
{\color{PhantomSlate}\small IE University \textbullet\ Supervisor: Alberto Mart\'in Izquierdo\par}
\vspace{1.2em}
{\footnotesize\color{PhantomCyan}\href{https://velocitatem.github.io/PHANTOM/}{\texttt{velocitatem.github.io/PHANTOM}}}
\vfill
\end{frame}
% =========================================================================
% ROADMAP (no minutes)
% =========================================================================
\begin{frame}{Roadmap: one argument in six stages}
\centering
\begin{tikzpicture}[
font=\scriptsize\sffamily,
stage/.style={draw=PhantomInk,rounded corners=3pt,fill=PhantomCyan!10,minimum width=2.05cm,minimum height=0.95cm,align=center},
flow/.style={-{Stealth[length=2.0mm,width=1.8mm]},line width=1pt,PhantomSlate}
]
\node[stage] (intro) {Two POVs};
\node[stage,right=0.30cm of intro] (platform) {Platform};
\node[stage,right=0.30cm of platform] (signal) {Signal};
\node[stage,right=0.30cm of signal] (drrl) {DR-RL};
\node[stage,right=0.30cm of drrl] (results) {Results};
\node[stage,right=0.30cm of results] (close) {Close};
\draw[flow,shorten <=2pt,shorten >=2pt] (intro.east) -- (platform.west);
\draw[flow,shorten <=2pt,shorten >=2pt] (platform.east) -- (signal.west);
\draw[flow,shorten <=2pt,shorten >=2pt] (signal.east) -- (drrl.west);
\draw[flow,shorten <=2pt,shorten >=2pt] (drrl.east) -- (results.west);
\draw[flow,shorten <=2pt,shorten >=2pt] (results.east) -- (close.west);
\end{tikzpicture}
\vspace{0.9em}
\begin{block}{Main research question}
How can dynamic pricing preserve margin integrity when transactions are
increasingly mediated by non-human agents?
\end{block}
\vspace{0.35em}
{\footnotesize Dynamic pricing has often been treated as a secondary
optimization layer; agent-mediated shopping turns it into a primary
margin-risk surface.}
\end{frame}
% =========================================================================
% TWO POVs - INTRO
% =========================================================================
\begin{frame}{This work cycles through two points of view}
\centering
\begin{tikzpicture}[
font=\small\sffamily,
box/.style={draw=PhantomInk,rounded corners=6pt,minimum width=5.2cm,minimum height=3.4cm,align=center}
]
\node[box,fill=PhantomCyan!12] (consumer) at (-3.4,0) {};
\node[box,fill=PhantomIndigo!12] (platform) at (3.4,0) {};
\bighumanicon{-3.4,0.95}
\bigplatformicon{3.4,0.95}
\node[font=\small,align=center,text=PhantomInk] at (-3.4,-0.45)
{\textbf{Consumer POV}\\[0.4em]
\footnotesize\textit{``how much does this item cost?''}\\[0.2em]
\footnotesize the question being asked};
\node[font=\small,align=center,text=PhantomInk] at (3.4,-0.45)
{\textbf{Platform POV}\\[0.4em]
\footnotesize\textit{``what does this session reveal?''}\\[0.2em]
\footnotesize the demand being inferred};
\draw[<->,thick,PhantomSlate] (consumer.east) -- node[above,font=\scriptsize,text=PhantomPaper]{price quote} node[below,font=\scriptsize,text=PhantomPaper]{behavior trail} (platform.west);
\end{tikzpicture}
\vspace{0.6em}
\begin{block}{Why two views}
The same transaction looks very different from each side. We will switch
between them to show where agent-mediation breaks the loop.
\end{block}
\end{frame}
% =========================================================================
% POV 1 - DIRECT
% =========================================================================
\begin{frame}{POV 1: the consumer asks the platform directly}
\centering
\begin{tikzpicture}[
font=\small\sffamily,
actor/.style={draw=PhantomInk,rounded corners=5pt,minimum width=2.6cm,minimum height=1.4cm,align=center,fill=PhantomPaper},
msg/.style={draw=PhantomInk!40,rounded corners=2pt,fill=PhantomCyan!8,inner sep=4pt,font=\scriptsize}
]
\node[actor,fill=PhantomCyan!14] (h) at (-4.5,0) {\\[0.6em]consumer};
\node[actor,fill=PhantomIndigo!14] (p) at (4.5,0) {\\[0.6em]platform};
\bighumanicon{-4.5,0.25}
\bigplatformicon{4.5,0.25}
\draw[-{Stealth[length=2.5mm]},thick,PhantomPaper] ([yshift=0.45cm]h.east) -- ([yshift=0.45cm]p.west)
node[midway,above,msg]{``how much does item $i$ cost?''};
\draw[-{Stealth[length=2.5mm]},thick,PhantomPaper] ([yshift=-0.45cm]p.west) -- ([yshift=-0.45cm]h.east)
node[midway,below,msg]{``it costs $p_t$''};
\end{tikzpicture}
\vspace{0.7em}
\begin{columns}[T,onlytextwidth]
\column{0.5\textwidth}
\footnotesize
\textbf{What you see:} a website opens, you read a price, you decide.\\[0.3em]
\textbf{What the platform sees:} clicks, hovers, dwell time --- a clean
behavioral fingerprint of one human session.
\column{0.45\textwidth}
\begin{block}{Variables}
\vardef{$i$}{the item being shopped (e.g. a hotel night)}
\vardef{$p_t$}{posted price at time $t$, in EUR per booking}
\end{block}
\end{columns}
\end{frame}
% =========================================================================
% POV 2 - VIA AGENT
% =========================================================================
\begin{frame}{POV 2: the consumer asks an AI agent}
\centering
\begin{tikzpicture}[
font=\small\sffamily,
actor/.style={draw=PhantomInk,rounded corners=5pt,minimum width=2.3cm,minimum height=1.3cm,align=center,fill=PhantomPaper},
msg/.style={font=\tiny,text=PhantomSlate}
]
\node[actor,fill=PhantomCyan!14] (h) at (-5.6,0) {};
\node[actor,fill=PhantomCyan!14] (a) at (0,0) {};
\node[actor,fill=PhantomIndigo!14] (p) at (5.6,0) {};
\bighumanicon{-5.6,0}
\bigroboticon{0,0}
\bigplatformicon{5.6,0}
\draw[-{Stealth[length=2.5mm]},thick,PhantomPaper] ([yshift=0.35cm]h.east) -- ([yshift=0.35cm]a.west)
node[midway,above,msg]{``find prices for $i$''};
\draw[-{Stealth[length=2.5mm]},thick,PhantomPaper] ([yshift=0.35cm]a.east) -- ([yshift=0.35cm]p.west)
node[midway,above,msg]{repeated queries};
\draw[-{Stealth[length=2.5mm]},thick,PhantomCyan] ([yshift=-0.35cm]p.west) -- ([yshift=-0.35cm]a.east)
node[midway,below,msg,text=PhantomCyan]{many quotes};
\draw[-{Stealth[length=2.5mm]},thick,PhantomCyan] ([yshift=-0.35cm]a.west) -- ([yshift=-0.35cm]h.east)
node[midway,below,msg,text=PhantomCyan]{best price found};
\end{tikzpicture}
\vspace{0.5em}
\begin{columns}[T,onlytextwidth]
\column{0.55\textwidth}
\footnotesize
\textbf{Same intent, different visible behavior.} The platform sees a
machine-paced session that looks nothing like the human who actually
wants to buy.
\column{0.40\textwidth}
\begin{alertblock}{Information asymmetry flips}
The agent samples many quotes before committing; the platform only
sees the recon, not the buyer.
\end{alertblock}
\end{columns}
\end{frame}
% =========================================================================
% DEMAND FLOW - WHAT THE PLATFORM ACTUALLY OBSERVES
% =========================================================================
\begin{frame}{Two flows, one demand signal --- and only one is reliable}
\centering
\begin{tikzpicture}[font=\scriptsize\sffamily,
flow/.style={-{Stealth[length=2.2mm]},thick,PhantomSlate},
panel/.style={draw=PhantomInk,rounded corners=4pt,minimum width=5.5cm,minimum height=3.0cm,align=center,fill=PhantomPaper}]
\node[panel,fill=PhantomCyan!8] (left) at (-3.4,0) {};
\node[anchor=north,font=\footnotesize\bfseries] at (left.north) {\vphantom{p}direct human session};
\coordinate (h1) at (-5.0,0.0);
\bighumanicon{h1}
\node[draw=PhantomInk,rounded corners=2pt,minimum width=1.4cm,minimum height=0.55cm,fill=PhantomPaper,font=\tiny] (pt1) at (-2.0,0.6) {one buyer};
\node[draw=PhantomInk,rounded corners=2pt,minimum width=1.4cm,minimum height=0.55cm,fill=PhantomCyan!20,font=\tiny] (q1) at (-2.0,-0.6) {true demand};
\draw[flow] ($(h1)+(0.3,0)$) -- (pt1.west);
\draw[flow] ($(h1)+(0.3,0)$) -- (q1.west);
\node[panel,fill=PhantomIndigo!8] (right) at (3.4,0) {};
\node[anchor=north,font=\footnotesize\bfseries] at (right.north) {\vphantom{p}agent-mediated session};
\coordinate (h2) at (1.4,0.0);
\coordinate (a2) at (3.4,0.0);
\bighumanicon{h2}
\bigroboticon{a2}
\node[draw=PhantomInk,rounded corners=2pt,minimum width=1.4cm,minimum height=0.55cm,fill=PhantomPaper,font=\tiny] (pt2) at (5.4,0.6) {many quotes};
\node[draw=PhantomInk,rounded corners=2pt,minimum width=1.4cm,minimum height=0.55cm,fill=PhantomMute!30,font=\tiny] (q2) at (5.4,-0.6) {buyer hidden};
\draw[flow] ($(h2)+(0.3,0)$) -- ($(a2)+(-0.42,0)$);
\draw[flow] ($(a2)+(0.42,0)$) -- (pt2.west);
\draw[flow] ($(a2)+(0.42,0)$) -- (q2.west);
\end{tikzpicture}
\vspace{0.5em}
{\footnotesize\textbf{Takeaway.} On the right, the platform sees recon, not
intent. Pricing trained on the visible signal will misread real demand.}
\end{frame}
% =========================================================================
% POLICY DEFINITION (variables explained)
% =========================================================================
\begin{frame}{Policy first: one rule maps context into a price}
\begin{columns}[T,onlytextwidth]
\column{0.50\textwidth}
\begin{block}{Definition}
\[
p_t = \pi(x_t)
\]
\end{block}
\vardef{$p_t$}{price quoted at time $t$ \;[EUR per booking]}
\vardef{$\pi$}{the pricing policy --- a function the platform learns}
\vardef{$x_t$}{context: product, time-of-day, and behavior summary of the session}
\column{0.45\textwidth}
\centering
\begin{tikzpicture}[
font=\scriptsize\sffamily,
box/.style={draw=PhantomInk,rounded corners=4pt,minimum width=3.35cm,minimum height=0.85cm,align=center},
flow/.style={-{Stealth[length=2.0mm]},thick,PhantomSlate}
]
\node[box,fill=PhantomPaper] (ctx) at (0,1.35) {context $x_t$};
\node[box,fill=PhantomIndigo!12] (pol) at (0,0.15) {policy $\pi$};
\node[box,fill=PhantomCyan!15] (act) at (0,-1.05) {price $p_t$};
\draw[flow] (ctx) -- (pol);
\draw[flow] (pol) -- (act);
\node[font=\tiny\itshape,text=PhantomSlate] at (0,-1.75) {bandits first; later extended to DR-RL};
\end{tikzpicture}
\end{columns}
\end{frame}
% =========================================================================
% COI METRIC (matches reference image style)
% =========================================================================
\begin{frame}{Cost of Information (COI) --- what the platform earns from \alert{knowing you}}
\begin{columns}[T,onlytextwidth]
\column{0.50\textwidth}
\begin{block}{Definition}
\vspace{0.2em}
\centering
\[
\mathrm{COI}(\pi) = \mathbb{E}[P] - p_{\mathrm{floor}}
\]
\end{block}
\vardef{$\mathbb{E}[P]$}{expected price the policy actually charges \;[EUR per booking]}
\vardef{$p_{\mathrm{floor}}$}{minimum viable price (marginal cost / break-even floor)}
\vardef{COI}{average premium the platform extracts above marginal cost \;[EUR per transaction]}
\column{0.48\textwidth}
\centering
\begin{tikzpicture}[font=\scriptsize\sffamily,x=0.95cm,y=0.85cm]
\draw[->,thick,PhantomPaper] (-0.2,0) -- (5.6,0) node[right,text=PhantomPaper]{price $p$};
\draw[->,thick,PhantomPaper] (0,-0.1) -- (0,3.0) node[above,text=PhantomPaper]{density};
\draw[very thick,PhantomPaper,domain=0.2:5.2,smooth,samples=80]
plot (\x, {2.5*exp(-((\x-2.6)^2)/1.0)});
\draw[dashed,PhantomSlate] (1.2,0) -- (1.2,2.0) node[above,font=\tiny,text=PhantomPaper]{$p_{\mathrm{floor}}$};
\draw[dashed,PhantomCyan,thick] (2.6,0) -- (2.6,2.5) node[above,font=\tiny,text=PhantomCyan]{$\mathbb{E}[P]$};
\draw[decorate,decoration={brace,amplitude=4pt},PhantomCyan,thick] (1.2,2.7) -- (2.6,2.7) node[midway,above,font=\scriptsize,text=PhantomCyan]{COI};
\end{tikzpicture}
\end{columns}
\vspace{0.3em}
{\footnotesize\textit{The ``cost'' is from the consumer's POV: it is what they pay because the platform can read their interest. Revenue at risk equals COI $\times$ volume.}}
\end{frame}
% =========================================================================
% WHY AGENTS ERODE COI - SIMPLIFIED ORDER STATISTIC
% =========================================================================
\begin{frame}{Why agents erode COI: the realizable price drops to the minimum}
\begin{columns}[T,onlytextwidth]
\column{0.46\textwidth}
\footnotesize
A single buyer pays the price they were quoted.\\[0.4em]
An agent samples $N$ independent quotes and the buyer pays
\(\;p^{(1)} = \min(p_1,\dots,p_N)\).\\[0.4em]
\textbf{Result:} as $N \to \infty$, $\mathrm{COI} \to 0$. More recon
pushes realizable prices toward the floor.
\vspace{0.5em}
\begin{alertblock}{One-line claim}
Untreated agentic recon behaves like an information leak that
compresses sustainable margins.
\end{alertblock}
\column{0.50\textwidth}
\centering
\begin{tikzpicture}[font=\scriptsize\sffamily,x=0.65cm,y=0.85cm]
\draw[->,thick,PhantomPaper] (0,0) -- (6.5,0) node[right,text=PhantomPaper]{queries $N$};
\draw[->,thick,PhantomPaper] (0,0) -- (0,2.6) node[above,text=PhantomPaper]{COI [EUR]};
\draw[very thick,PhantomCyan,domain=0.2:6.0,smooth,samples=60]
plot (\x, {2.2*exp(-0.55*\x)+0.12});
\draw[dashed,PhantomSlate] (0,0.12) -- (6.0,0.12);
\node[anchor=west,font=\tiny,text=PhantomSlate] at (3.4,0.32) {price-floor proximity};
\node[anchor=west,font=\tiny,text=PhantomPaper] at (0.4,2.2) {single human};
\node[anchor=west,font=\tiny,text=PhantomCyan] at (3.4,1.0) {agent-amplified};
\end{tikzpicture}
\end{columns}
\end{frame}
% =========================================================================
% RESEARCH QUESTIONS - SQ1 SQ2 SQ3
% =========================================================================
\begin{frame}{The thesis answers one chain: \alert{mechanism \(\to\) signal \(\to\) control}}
\begin{columns}[T,onlytextwidth]
\column{0.32\textwidth}
\centering
\begin{tikzpicture}[font=\scriptsize\sffamily]
\draw[rounded corners=4pt,draw=PhantomInk,fill=PhantomPaper] (-1.55,-1.1) rectangle (1.55,1.2);
\fill[PhantomCyan] (-0.75,0.35) circle (0.14);
\fill[PhantomCyan] (-0.45,0.70) circle (0.14);
\fill[PhantomCyan] (-0.15,0.45) circle (0.14);
\fill[PhantomIndigo] (0.35,-0.20) circle (0.14);
\fill[PhantomIndigo] (0.65,-0.45) circle (0.14);
\fill[PhantomIndigo] (0.95,-0.15) circle (0.14);
\draw[dashed,PhantomInk!60] (0.12,-0.92) -- (0.12,1.0);
\node[text=PhantomMute,font=\tiny] at (0,-0.97) {behavior separability};
\end{tikzpicture}
{\footnotesize\textbf{SQ1}}\\[-0.15em]
{\scriptsize Can we distinguish \humanicon and \roboticon sessions from interactions alone?}
\column{0.32\textwidth}
\centering
\begin{tikzpicture}[font=\scriptsize\sffamily]
\draw[rounded corners=4pt,draw=PhantomInk,fill=PhantomPaper] (-1.55,-1.1) rectangle (1.55,1.2);
\draw[->,thick,PhantomMute] (-1.15,-0.75) -- (1.2,-0.75) node[right,font=\tiny,text=PhantomMute,xshift=-3pt,yshift=4pt]{$\alpha$};
\draw[->,thick,PhantomMute] (-1.15,-0.75) -- (-1.15,0.85) node[above,font=\tiny,text=PhantomMute,yshift=-4pt,xshift=8pt]{COI};
\draw[very thick,PhantomCyan,domain=-1.0:1.05,smooth,samples=40] plot (\x, {0.65*exp(-1.2*(\x+1.0))-0.05});
\draw[dashed,PhantomMute] (-1.15,-0.55) -- (1.05,-0.55);
\node[text=PhantomMute,font=\tiny,xshift=2pt] at (-0.4,-0.45) {floor};
\node[text=PhantomMute,font=\tiny] at (0,-0.97) {COI erodes as $\alpha\uparrow$};
\end{tikzpicture}
{\footnotesize\textbf{SQ2}}\\[-0.15em]
{\scriptsize How strong is price and revenue erosion under agentic contamination?}
\column{0.32\textwidth}
\centering
\begin{tikzpicture}[font=\scriptsize\sffamily]
\draw[rounded corners=4pt,draw=PhantomInk,fill=PhantomPaper] (-1.55,-1.1) rectangle (1.55,1.2);
\draw[->,thick,PhantomMute] (-1.15,-0.75) -- (1.2,-0.75) node[right,font=\tiny,text=PhantomMute,xshift=-3pt,yshift=4pt]{UX};
\draw[->,thick,PhantomMute] (-1.15,-0.75) -- (-1.15,0.85) node[above,font=\tiny,text=PhantomMute,yshift=-4pt,xshift=10pt]{margin};
\fill[PhantomSlate] (-0.55,-0.40) circle (2.6pt);
\node[text=PhantomMute,font=\tiny] at (-0.55,-0.13) {baseline};
\fill[PhantomCyan] (0.55,0.45) circle (2.8pt);
\node[text=PhantomCyan,font=\tiny] at (0.55,0.72) {robust};
\draw[->,thick,PhantomCyan,dashed] (-0.40,-0.30) -- (0.40,0.35);
\node[text=PhantomMute,font=\tiny] at (0,-0.97) {robust dominates baseline};
\end{tikzpicture}
{\footnotesize\textbf{SQ3}}\\[-0.15em]
{\scriptsize Can policy design recover margin while keeping UX stable?}
\end{columns}
\end{frame}
\section{Platform Development}
% =========================================================================
% PLATFORM (Stage 1)
% =========================================================================
\begin{frame}{Stage 1: a dual-loop platform pairs every quote with its behavior}
\centering
\begin{tikzpicture}[
font=\scriptsize\sffamily,
box/.style={draw=PhantomInk,rounded corners=3pt,minimum width=2.5cm,minimum height=0.9cm,align=center,fill=PhantomPaper},
arr/.style={-{Stealth[length=2.2mm]},thick,PhantomSlate}
]
\node[box,fill=PhantomCyan!14] (actors) at (0,1.45) {users \humanicon\\agents \roboticon};
\node[box] (web) at (2.9,1.45) {web\\storefront};
\node[box] (provider) at (5.8,1.45) {pricing\\service};
\node[box] (redis) at (8.7,1.45) {serve\\cache};
\node[box,fill=PhantomIndigo!12,minimum width=3.1cm] (kafka) at (4.35,-0.15) {event stream\\behavior + quote logs};
\node[box,fill=PhantomCyan!10,minimum width=2.8cm] (airflow) at (8.0,-0.15) {offline trainer\\batch updates};
\draw[arr] (actors) -- (web);
\draw[arr] (web) -- (provider);
\draw[arr] (provider) -- (redis);
\draw[arr] (web.south) -- (kafka.north west);
\draw[arr] (provider.south) -- (kafka.north east);
\draw[arr] (kafka) -- (airflow);
\draw[arr] (airflow.north) -| (redis.south);
\draw[arr] (redis.west) to[bend left=35] (provider.east);
\node[font=\tiny\itshape,text=PhantomSlate] at (2.2,-1.0) {Kappa: streaming telemetry};
\node[font=\tiny\itshape,text=PhantomSlate] at (8.1,-1.0) {Lambda: offline learning + refresh};
\end{tikzpicture}
\vspace{0.4em}
\begin{itemize}
\item Every quote has a matching behavioral context in the log stream.
\item The same architecture supports reproducible stress tests before any live deployment.
\end{itemize}
\end{frame}
% =========================================================================
% DATASET CARD
% =========================================================================
\begin{frame}{Dataset card: compact, labeled, experiment-ready}
\begin{columns}[T,onlytextwidth]
\column{0.60\textwidth}
\centering
\begin{tikzpicture}[
font=\scriptsize\sffamily,
chip/.style={draw=PhantomInk!40,rounded corners=2pt,inner sep=2.7pt,fill=PhantomPaper}
]
\node[draw=PhantomInk,rounded corners=5pt,fill=PhantomPaper,minimum width=6.85cm,minimum height=4.45cm] at (0,0) {};
\node[anchor=west,font=\footnotesize\bfseries,text=PhantomInk] at (-3.2,1.72) {WhoClickedIt dataset card};
\node[anchor=west,draw=PhantomInk!35,rounded corners=2pt,fill=PhantomCyan!10,inner xsep=4pt,inner ysep=3pt,text width=6.15cm,align=left,font=\tiny\ttfamily,text=PhantomInk] at (-3.2,1.22)
{huggingface.co/datasets/velocitatem/whoclickedit};
\node[anchor=west,chip,fill=PhantomIndigo!12] (humanrows) at (-3.2,0.52) {\textbf{human rows} 798};
\node[anchor=west,chip,fill=PhantomIndigo!12] at ([xshift=0.16cm]humanrows.east) {\textbf{agent rows} 3076};
\node[anchor=west,text width=6.0cm,align=left,font=\scriptsize,text=PhantomInk] at (-3.2,-0.33)
{Flat schema and explicit actor labels simplify session-aware train/test splits.};
\node[anchor=west,font=\tiny\itshape,text=PhantomSlate] at (-3.2,-1.01)
{Kafka provenance is retained for reproducibility.};
\end{tikzpicture}
\column{0.38\textwidth}
\centering
\begin{tikzpicture}[font=\scriptsize\sffamily,
stat/.style={draw=PhantomInk,rounded corners=5pt,minimum width=4.95cm,minimum height=1.33cm,align=center}]
\node[stat,fill=PhantomCyan!12] at (0,1.95)
{\Large\bfseries 29 interviews\\[-0.1em]\footnotesize labeled trajectories};
\node[stat,fill=PhantomCyan!18] at (0,0.25)
{\Large\bfseries 45\% / 55\%\\[-0.1em]\footnotesize human / agent split};
\node[stat,fill=PhantomIndigo!12] at (0,-1.45)
{\Large\bfseries 2 streams\\[-0.1em]\footnotesize interaction + price logs};
\end{tikzpicture}
\end{columns}
\end{frame}
% =========================================================================
% EXPERIMENTAL DESIGN
% =========================================================================
\begin{frame}{Experimental design controls goals, not instructions}
\begin{columns}[T,onlytextwidth]
\column{0.58\textwidth}
\centering
\begin{tikzpicture}[
font=\scriptsize\sffamily,
box/.style={draw=PhantomInk,rounded corners=3pt,minimum width=3.65cm,minimum height=0.95cm,align=center,fill=PhantomPaper},
arr/.style={-{Stealth[length=2.2mm]},thick,PhantomSlate}
]
\node[box,fill=PhantomCyan!14] (tasks) at (0,1.8) {JTBD task pool\\hotel + airline modes};
\node[box] (assign) at (0,0.55) {random assignment\\mode + task + actor id};
\node[box,fill=PhantomIndigo!12] (run) at (0,-0.7) {execution\\human or browser-use agent};
\node[box] (logs) at (0,-1.95) {session logs\\events + quotes};
\draw[arr] (tasks) -- (assign);
\draw[arr] (assign) -- (run);
\draw[arr] (run) -- (logs);
\end{tikzpicture}
\column{0.40\textwidth}
\begin{itemize}\setlength{\itemsep}{0.55em}
\item Agents run with \textbf{browser-use} and a model-swappable LLM router (default \texttt{gpt-5-mini}).
\item Tasks are defined by outcomes, not scripted clicks, to preserve behavioral variety.
\item Current release is stronger on hotel flows than airline flows.
\end{itemize}
\end{columns}
\end{frame}
\section{Distinguishability Construction}
% =========================================================================
% KERNEL EXPLAINER (NEW dedicated slide)
% =========================================================================
\begin{frame}{Stage 2: what is a \alert{kernel}?}
\begin{block}{Plain definition}
A \textbf{kernel} is a small square table $T$ where $T[a,b]$ is the
probability that action $b$ follows action $a$ inside one session. Every
row sums to one.
\end{block}
\vspace{0.3em}
\begin{columns}[T,onlytextwidth]
\column{0.45\textwidth}
\centering
\begin{tikzpicture}[font=\scriptsize\sffamily]
\node[draw=PhantomInk,rounded corners=3pt,fill=PhantomCyan!12,minimum width=2.6cm,minimum height=0.7cm] (a) at (-1.6,0.7) {view item};
\node[draw=PhantomInk,rounded corners=3pt,fill=PhantomPaper,minimum width=2.6cm,minimum height=0.7cm] (b) at (1.6,0.7) {hover};
\node[draw=PhantomInk,rounded corners=3pt,fill=PhantomIndigo!12,minimum width=2.6cm,minimum height=0.7cm] (c) at (0,-0.9) {add to cart};
\draw[-{Stealth[length=2mm]},thick,PhantomSlate] (a) -- node[above,font=\tiny]{0.64} (b);
\draw[-{Stealth[length=2mm]},thick,PhantomSlate] (b) -- node[right,font=\tiny]{0.31} (c);
\draw[-{Stealth[length=2mm]},thick,PhantomSlate] (a) -- node[left,font=\tiny]{0.36} (c);
\end{tikzpicture}\\[0.3em]
{\tiny example session graph}
\column{0.50\textwidth}
\centering
\begin{tabular}{@{}l|ccc@{}}
\toprule
from \textbackslash{} to & view & hover & cart \\
\midrule
view & 0.00 & 0.64 & 0.36 \\
hover & 0.69 & 0.00 & 0.31 \\
cart & 0.00 & 0.00 & 1.00 \\
\bottomrule
\end{tabular}\\[0.4em]
{\footnotesize\textit{This is the kernel $T$. Each row is a probability distribution.}}
\end{columns}
\vspace{0.3em}
\vardef{$T[a,b]$}{probability that the next action is $b$ given the current action is $a$}
\end{frame}
% =========================================================================
% HUMAN VS AGENT KERNELS - VISUAL COMPARISON
% =========================================================================
\begin{frame}{Humans and agents click in \alert{different patterns}}
\begin{columns}[T,onlytextwidth]
\column{0.48\textwidth}
\centering
\textbf{Human kernel $\bar T_H$}\par
{\scriptsize view $\to$ hover $\to$ cart, with detours}\par\vspace{0.2em}
\includegraphics[width=\linewidth,height=0.40\textheight,keepaspectratio]{mdp_human.pdf}
\column{0.48\textwidth}
\centering
\textbf{Agent kernel $\bar T_A$}\par
{\scriptsize view $\to$ view $\to$ view, almost no cart}\par\vspace{0.2em}
\includegraphics[width=\linewidth,height=0.40\textheight,keepaspectratio]{mdp_agent.pdf}
\end{columns}
\vspace{0.4em}
\begin{columns}[T,onlytextwidth]
\column{0.32\textwidth}\centering\metriccard{$-3.35$}{mean gap (human)}
\column{0.32\textwidth}\centering\metriccard{$+1.65$}{mean gap (agent)}
\column{0.32\textwidth}\centering\metriccard{$p<0.001$}{Mann-Whitney rank}
\end{columns}
\vspace{0.2em}
{\footnotesize\textit{Two cohorts, two clearly separable click structures --- this is the foundation of the detection signal.}}
\end{frame}
% =========================================================================
% SIGMOID SCORE - SIMPLIFIED
% =========================================================================
\begin{frame}{From two divergences to one \alert{sigmoid score}}
\begin{columns}[T,onlytextwidth]
\column{0.46\textwidth}
\begin{block}{Step 1 --- distance to each prototype}
\(\Delta_H = \mathrm{KL}(\hat T' \,\|\, \bar T_H)\)\\[0.2em]
\(\Delta_A = \mathrm{KL}(\hat T' \,\|\, \bar T_A)\)
\end{block}
\begin{block}{Step 2 --- signed gap}
\(g(\tau') = \Delta_H - \Delta_A\)
\end{block}
\begin{alertblock}{Step 3 --- \textbf{sigmoid} squash}
\(f(\tau') = \sigma\!\left(\dfrac{g(\tau')}{T}\right) \in [0,1]\)
\end{alertblock}
\vardef{$\sigma$}{the standard logistic sigmoid}
\vardef{$T$}{temperature, controls how sharply the score moves away from $0.5$}
\vardef{threshold}{$f = 0.5$ corresponds to $g = 0$ (neither side)}
\column{0.50\textwidth}
\centering
\begin{tikzpicture}[font=\scriptsize\sffamily,x=0.7cm,y=2.0cm]
\draw[->,thick,PhantomPaper] (-4.2,0) -- (4.4,0) node[right,text=PhantomPaper]{$g(\tau')$};
\draw[->,thick,PhantomPaper] (0,-0.05) -- (0,1.15) node[above,text=PhantomPaper]{$f(\tau')$};
\draw[dashed,PhantomMute] (-4.0,1.0) -- (4.0,1.0);
\draw[dashed,PhantomCyan,thick] (-4.0,0.5) -- (4.0,0.5) node[right,font=\tiny,text=PhantomCyan]{threshold $0.5$};
\draw[very thick,PhantomCyan,domain=-4.0:4.0,smooth,samples=120]
plot (\x, {1/(1+exp(-1.4*\x))});
\node[anchor=north,font=\tiny,text=PhantomPaper] at (-3.0,-0.04) {human-like \humanicon};
\node[anchor=north,font=\tiny,text=PhantomPaper] at (3.0,-0.04) {agent-like \roboticon};
\fill[PhantomCyan] (0,0.5) circle (1.6pt);
\node[anchor=west,font=\tiny,text=PhantomPaper] at (0.1,0.55) {$g=0$};
\end{tikzpicture}\\[0.2em]
{\tiny\color{PhantomSlate} logistic curve --- saturates at $0$ and $1$, threshold at $0.5$}
\end{columns}
\end{frame}
\section{Distributionally Robust RL}
% =========================================================================
% DR-RL CORE IDEA - SIMPLIFIED
% =========================================================================
\begin{frame}{Stage 3: DR-RL trains against \alert{many plausible worlds}}
\begin{columns}[T,onlytextwidth]
\column{0.50\textwidth}
\footnotesize
Standard RL trains against one demand model and overfits to it.
\textbf{DR-RL} optimises the worst case across a small ball of
plausible demand laws, so the policy still works when contamination
shifts.
\vspace{0.6em}
\begin{block}{Robust objective}
\(\displaystyle \pi^\star = \arg\max_\pi \min_{Q \in U_\epsilon} \mathbb{E}_Q[\,r\,]\)
\end{block}
\vardef{$Q$}{a candidate demand distribution inside the ball}
\vardef{$U_\epsilon$}{Wasserstein ball of radius $\epsilon$ around the empirical $\hat P_N$}
\vardef{$r$}{per-step reward (defined next slide)}
\column{0.46\textwidth}
\centering
\begin{tikzpicture}[font=\scriptsize\sffamily]
\draw[thick,PhantomPaper,fill=PhantomCyan!25!PhantomInk] (0,0) circle (2.0);
\draw[dashed,PhantomCyan,thick] (0,0) circle (1.4);
\fill[PhantomPaper] (0,0) circle (2.5pt) node[below,font=\tiny,text=PhantomPaper]{$\hat P_N$};
\fill[PhantomCyan] (0.9,0.5) circle (2pt);
\fill[PhantomCyan] (-0.7,0.7) circle (2pt);
\fill[PhantomCyan] (-1.0,-0.4) circle (2pt);
\fill[PhantomCyan] (0.6,-0.9) circle (2pt);
\node[font=\tiny,text=PhantomCyan] at (0,1.7) {plausible $Q$};
\node[font=\tiny,text=PhantomPaper] at (0,-2.3) {ambiguity ball $U_\epsilon$};
\end{tikzpicture}
\end{columns}
\vspace{0.3em}
{\footnotesize\textit{Implementation note: in code we solve a local robust loop on the contamination parameter $\alpha$, not the full continuous Wasserstein adversary.}}
\end{frame}
% =========================================================================
% REWARD - THREE TERMS, EACH EXPLAINED
% =========================================================================
\begin{frame}{Reward: revenue, minus leakage, minus UX cost}
\[
r_t \;=\; \underbrace{R(p_t,\hat Q_t)}_{\text{revenue}}
\;-\; \underbrace{\lambda \, f(\tau'_t)\, c_{\text{info}}}_{\text{leakage penalty}}
\;-\; \underbrace{\eta_{\text{ux}}\, \mathrm{UX}(\tau'_t,p_t)}_{\text{UX penalty}}
\]
\vspace{0.6em}
\begin{columns}[T,onlytextwidth]
\column{0.32\textwidth}
\begin{block}{Revenue}
\(R(p_t,\hat Q_t) = p_t \cdot \hat Q_t\)\\[0.2em]
{\footnotesize units: EUR per session}
\end{block}
\column{0.32\textwidth}
\begin{alertblock}{Leakage}
scales with $f(\tau'_t)$ \roboticon\\
$\lambda$: weight\\
$c_{\text{info}}$: per-query cost
\end{alertblock}
\column{0.32\textwidth}
\begin{exampleblock}{UX}
$\mathrm{UX}\in[0,1]$\\
$\eta_{\text{ux}}$: weight\\
penalises unstable pricing
\end{exampleblock}
\end{columns}
\vspace{0.4em}
{\footnotesize \textbf{Reading the formula:}
if a session looks agent-like ($f \uparrow$), the leakage penalty grows
and the policy backs off; for clean human sessions only the revenue and
UX terms are active.}
\end{frame}
% =========================================================================
% COMPUTE
% =========================================================================
\begin{frame}{Wide sweeps are feasible only with \alert{aggressive optimization}}
\begin{columns}[T,onlytextwidth]
\column{0.47\textwidth}
\centering
{\Large\(4\times4\times3\times2\times2=\mathbf{192}\) configs}\\[0.25em]
{\scriptsize algorithms $\times$ contamination $\times$ robustness $\times$ COI penalty $\times$ action grid}
\vspace{0.5em}
\metriccard{160 PFLOPS}{peak aggregate TPU budget}\\[0.45em]
\metriccard{$\sim$180 days}{net compute logged}
\column{0.51\textwidth}
\begin{block}{Hot-path rewrite impact}
\centering
\begin{tabular}{@{}lcc@{}}
\toprule
Mode & Before & After \\
\midrule
Baseline step/s & 26.0 & 220.0 \\
Robust step/s & 7.2 & 136.0 \\
\bottomrule
\end{tabular}
\end{block}
\vspace{0.1em}
{\footnotesize
\begin{itemize}
\item pandas lookups replaced with array/JAX-style loops.
\item $8.5\times$ and $19\times$ throughput gains made wide sweeps practical.
\end{itemize}}
\end{columns}
\end{frame}
\section{Results}
% =========================================================================
% RESULTS - BIGGER FONTS, EXPLICIT CONCLUSION
% =========================================================================
\begin{frame}{Defended policies recover COI as contamination rises}
\begin{columns}[T,onlytextwidth]
\column{0.62\textwidth}
\centering
\includegraphics[width=\linewidth,height=0.66\textheight,keepaspectratio]{final_focus_coi_by_alpha.pdf}\\[0.2em]
{\footnotesize x: contamination $\alpha$ (fraction of agent traffic) \;|\; y: COI [EUR per transaction]}
\column{0.34\textwidth}
\metriccard{$-90{,}140$}{baseline COI slope (EUR per unit $\alpha$)}\\[0.35em]
\metriccard{$\sim 3\%$}{short-run revenue cost of defense}\\[0.35em]
\metriccard{regime-dependent}{COI gains strongest at higher $\alpha$}
\vspace{0.5em}
\begin{alertblock}{Conclusion}
\footnotesize Without defense, COI collapses with $\alpha$.
Robust policies hold a measurable margin floor at the cost of
a small, bounded revenue trade.
\end{alertblock}
\end{columns}
\end{frame}
\section{Conclusions}
% =========================================================================
% CONCLUSION
% =========================================================================
\begin{frame}{Yes, with boundaries: margin integrity \alert{is defensible} under agentic orchestration}
\begin{columns}[T,onlytextwidth]
\column{0.32\textwidth}
\begin{block}{SQ1\;\;distinguishability}
\centering
kernels are separable\\$p<0.001$
\end{block}
\column{0.32\textwidth}
\begin{block}{SQ2\;\;theoretical impact}
\centering
COI erosion mechanism\\proved in baseline limit
\end{block}
\column{0.32\textwidth}
\begin{block}{SQ3\;\;mitigation}
\centering
robust control shifts\\COI / revenue / UX trade-off
\end{block}
\end{columns}
\vspace{0.5em}
\begin{alertblock}{Boundary conditions}
Evidence is from a controlled platform and a small labeled cohort.
This is mechanism validation, not full production external validity.
\end{alertblock}
\end{frame}
% =========================================================================
% IMPLICATIONS
% =========================================================================
\begin{frame}{What this implies for real pricing systems}
\begin{itemize}\setlength{\itemsep}{0.7em}
\item \textbf{Financially:} untreated reconnaissance behaves like an information leak and can compress sustainable margins.
\item \textbf{Operationally:} behavior-only session scoring can be wired into pricing without device fingerprinting.
\item \textbf{Market exposure:} channels where dynamic pricing is a secondary layer (aggregators, comparison funnels, promo traffic) are disrupted first.
\item \textbf{Strategically:} robust pricing should be calibrated by regime; there is no single penalty that wins everywhere.
\item \textbf{Before deployment:} larger human baselines, governance review, and legal safeguards are mandatory.
\end{itemize}
\end{frame}
% =========================================================================
% THANK YOU
% =========================================================================
\begin{frame}[plain]
\centering
\vfill
{\LARGE\bfseries Thank you}
\vspace{0.8em}
{\large Questions and discussion}
\vfill
{\footnotesize\color{PhantomSlate}Appendix follows: COI theorem derivation, reward composition, and sample-size notes.}
\vfill
\end{frame}
\appendix
\input{defense_appendix}
\end{document}

View File

@@ -0,0 +1,364 @@
% Included by defense.tex after the main deck (extensive appendix).
\section{Appendix}
\begin{frame}{Appendix roadmap}
\footnotesize
\begin{columns}[T,onlytextwidth]
\column{0.31\textwidth}
\begin{block}{A.\ Objects}
Notation, COI, proxies
\end{block}
\column{0.31\textwidth}
\begin{block}{B.\ Mechanism}
Order stats, kernels, KL
\end{block}
\column{0.31\textwidth}
\begin{block}{C.\ Control}
Simulator, robust loop, factorial grid
\end{block}
\end{columns}
\vfill
\begin{alertblock}{Figures}
Full charts, MDPs, extra revenue view
\end{alertblock}
\end{frame}
% ----- A. Notation & definitions -----
\begin{frame}{Appendix: core notation (quick reference, I)}
\scriptsize
\begin{align*}
\tau_s &= (e_{s,1},\ldots,e_{s,L_s}) && \text{session} \\
\hat{q}_{t,i} &= \sum_{s\in S_t}\sum_k \omega(a_{s,k})\,\mathbf{1}[i_{s,k}=i] && \text{proxy }(\humanicon, \roboticon) \\
Q(p) &= (1-\alpha)\,\mathbb{E}_{\theta\sim D_H}[d(p;\theta)] \\
&\quad + \alpha\,\mathbb{E}_{\theta\sim D_A}[d(p;\theta)] + \epsilon_t && \text{mixture of }\humanicon/\roboticon \\
\mathrm{COI}(\pi) &= \mathbb{E}[P]-\underline{p} && \text{COI}
\end{align*}
\end{frame}
\begin{frame}{Appendix: core notation (quick reference, II)}
\footnotesize
\begin{itemize}
\item \(\underline{p}\): minimum viable price anchor (thesis simplification).
\item \(\alpha\): contamination with agent traffic in the mixture.
\item \(\omega(a)\): hand-engineered action weights for the proxy (baseline).
\end{itemize}
\begin{alertblock}{Reading guide}
Objects on the left are \textbf{observable}; \(d(\cdot)\) and many \(\theta\) remain hidden.
\end{alertblock}
\end{frame}
\begin{frame}{Appendix: COI as a reporting functional}
\[
\mathrm{COI}(\pi) = \mathbb{E}_{P\sim F_\pi}[P] - \underline{p}
\]
\begin{block}{Interpretation}
Premium above the floor induced by policy \(\pi\); used as a KPI and as the object Theorem 1 attacks under query saturation.
\end{block}
\end{frame}
\begin{frame}{Appendix: demand proxy vs.\ latent demand}
\[
\hat{q}_{t,i}=\sum_{s\in S_t}\sum_{k=1}^{L_s} \omega(a_{s,k})\,\mathbf{1}[i_{s,k}=i]
\]
\begin{alertblock}{Key distinction}
\(\hat{q}\) is an operational sensor from logs (\humanicon, \roboticon); true demand \(d(p;\theta)\) stays latent. Pricing reacts to \(\hat{q}\), so agent-shaped behavior can poison the signal.
\end{alertblock}
\end{frame}
% ----- B. Mechanism -----
\begin{frame}{Appendix: independent draws and order statistics (intuition)}
\begin{columns}[T]
\column{0.55\textwidth}
\begin{itemize}
\item Independent price draws \(\{P_i\}_{i=1}^N\) from fixed offer law.
\item Purchase-side minimum behaves like \(P_{(1)}\): mass shifts left as \(N\) grows.
\item Expected premium vs.\ \(\underline{p}\) compresses: COI pressure.
\end{itemize}
\column{0.42\textwidth}
\centering
\begin{tikzpicture}[scale=0.85]
\draw[->,thick] (0,0)--(3.2,0) node[right] {\small queries \(N\)};
\draw[->,thick] (0,0)--(0,2.2) node[above] {\small COI};
\draw[PhantomCyan,very thick] (0.2,2) .. controls (1.5,1.2) and (2.2,0.5) .. (3,0.15);
\node[below right] at (2.4,0.6) {\footnotesize saturation};
\end{tikzpicture}
\end{columns}
\end{frame}
\begin{frame}{Appendix: Theorem 1 scope (what is and is not claimed)}
\small
\begin{block}{Inside the baseline proof}
Non-collusive sessions, independent draws, fixed offer distribution across queries.
\end{block}
\begin{alertblock}{Outside (handled elsewhere)}
Collusion, pooled recon, sequential repricing that breaks iid structure: evidence moves to the simulator.
\end{alertblock}
\end{frame}
\begin{frame}{Appendix: empirical transition kernel (MLE)}
\[
\hat{P}(s'\mid s)=\frac{N(s,s')}{\sum_k N(s,k)}
\]
\begin{block}{Use}
Human and agent centroids \(\bar{T}_H,\bar{T}_A\) for divergence-to-prototype scores.
\end{block}
\end{frame}
\begin{frame}{Appendix: KL to prototypes (shared support)}
\[
\Delta_H = D_{\mathrm{KL}}(\hat{T}'\,\|\,\bar{T}_H),\qquad
\Delta_A = D_{\mathrm{KL}}(\hat{T}'\,\|\,\bar{T}_A)
\]
\begin{exampleblock}{Asymmetric choice}
KL measures deviation from the \textbf{human} reference; symmetric JS/Wasserstein on behavior was not the design target.
\end{exampleblock}
\end{frame}
\begin{frame}{Appendix: softmax to sigmoid (algebra)}
\small
Let \(z_A=-\Delta_A/T\), \(z_H=-\Delta_H/T\). Then
\begin{align*}
P(A\mid\tau) &= \frac{e^{z_A}}{e^{z_A}+e^{z_H}}
= \frac{1}{1+e^{z_H-z_A}}
= \sigma\bigl(z_A-z_H\bigr) \\
&= \sigma\!\left(\frac{\Delta_H-\Delta_A}{T}\right).
\end{align*}
\begin{block}{Takeaway}
Two-class softmax over \((z_A,z_H)\) is exactly one sigmoid on the gap \((\Delta_H-\Delta_A)\).
\end{block}
\end{frame}
\begin{frame}{Appendix: contamination generator \(\mathcal{G}(\alpha)\)}
\[
\mathcal{G}(\alpha):\ \text{inject synthetic agent trajectories until mixture reaches target }\alpha
\]
\begin{alertblock}{Role in the lab}
Supplies controlled stress tests for the pricing learner; not a claim of production-faithful agents.
\end{alertblock}
\end{frame}
% ----- C. Robust control -----
\begin{frame}{Appendix: Wasserstein ambiguity (ideal object)}
\[
\mathcal{U}_\epsilon(\hat{P}_N)=\left\{ Q:\ W_p(Q,\hat{P}_N)\le \epsilon \right\}
\]
\begin{block}{What the code implements instead}
A \textbf{local} grid over \(\alpha\) near \(\alpha_0\) with radius \(\epsilon_\alpha\): tractable inner worst case, not a full ball solver.
\end{block}
\end{frame}
\begin{frame}{Appendix: per-step reward sketch}
\small
\[
r = R(p,d) - \lambda\,\mathrm{COI}_{\mathrm{leak}}(p,\tau') - \eta\,\mathrm{UX}(\tau',p) - \text{(supra-competitive excess)}
\]
\begin{itemize}
\item Query-tax style \(\mathrm{COI}_{\mathrm{leak}}\): minimal nonzero surrogate to expose the control channel.
\item UX and anchor penalties prevent trivial solutions (flat but exploitative prices).
\end{itemize}
\end{frame}
\begin{frame}{Appendix: factorial design (192 cells)}
\footnotesize
\centering
\begin{tabular}{@{}llr@{}}
\toprule
Axis & Levels & Count \\
\midrule
RL algorithm & PPO, A2C, DQN, Q-table & 4 \\
Contamination \(\alpha\) & 4 representative values in \([0.1,0.6]\) & 4 \\
Robustness radius \(\epsilon_\alpha\) & 3 & 3 \\
COI penalty \(\lambda_{\mathrm{coi}}\) & 2 & 2 \\
Action granularity & 2 & 2 \\
\midrule
\textbf{Total} & & \(4\times4\times3\times2\times2=\mathbf{192}\) \\
\bottomrule
\end{tabular}
\end{frame}
\begin{frame}{Appendix: engineering note (pandas \(\to\) JAX)}
\begin{itemize}
\item Hot path was label-indexed transition lookups; profiling showed pandas overhead dominated.
\item Integer-indexed arrays + JAX inner loop: large step/s throughput (thesis numbers; environment dependent).
\item Kronecker expansion of product-conditioned kernels: research simulator cost, scales with catalog.
\end{itemize}
\end{frame}
% ----- Extended figures (all PDFs in repo) -----
\begin{frame}{Appendix figure: COI by \(\alpha\) (full)}
\centering
\includegraphics[width=0.92\linewidth,height=0.78\textheight,keepaspectratio]{final_focus_coi_by_alpha.pdf}
\end{frame}
\begin{frame}{Appendix figure: revenue deltas (full)}
\centering
\includegraphics[width=0.92\linewidth,height=0.78\textheight,keepaspectratio]{final_focus_revenue_delta.pdf}
\end{frame}
\begin{frame}{Appendix figure: revenue by \(\alpha\) (full)}
\centering
\includegraphics[width=0.92\linewidth,height=0.78\textheight,keepaspectratio]{final_focus_revenue_by_alpha.pdf}
\end{frame}
\begin{frame}{Appendix figure: risk / stability deltas (full)}
\centering
\includegraphics[width=0.92\linewidth,height=0.78\textheight,keepaspectratio]{final_focus_risk_deltas.pdf}
\end{frame}
\begin{frame}{Appendix figure: COI preservation grid (full)}
\centering
\includegraphics[width=0.92\linewidth,height=0.78\textheight,keepaspectratio]{final_focus_coi_preservation_grid.pdf}
\end{frame}
\begin{frame}{Appendix figure: human MDP (full)}
\centering
\includegraphics[width=0.75\linewidth,height=0.82\textheight,keepaspectratio]{mdp_human.pdf}
\end{frame}
\begin{frame}{Appendix figure: agent MDP (full)}
\centering
\includegraphics[width=0.75\linewidth,height=0.82\textheight,keepaspectratio]{mdp_agent.pdf}
\end{frame}
% ----- Threat model & evaluation -----
\begin{frame}{Appendix: threat model map}
\centering
\resizebox{0.98\linewidth}{!}{%
\begin{tikzpicture}[
font=\sffamily\footnotesize,
box/.style={draw=PhantomInk,rounded corners=2pt,thick,align=center,inner sep=5pt,minimum width=2.8cm},
arr/.style={-Stealth,thick,PhantomSlate}
]
\node[box,fill=PhantomCyan!18] (A) at (0,0) {\textbf{Focus}\\[0.15em]browser agents\\into \(\hat{q}\)};
\node[box,fill=white] (B) at (3.8,0) {\textbf{Complementary}\\[0.15em]WAF, CAPTCHA,\\rate limits};
\node[box,fill=white] (C) at (7.6,0) {\textbf{Upstream}\\[0.15em]API scrape,\\no UI semantics};
\draw[arr] (A) -- node[above] {\tiny scope} (B);
\draw[arr] (B) -- node[above] {\tiny out of scope} (C);
\end{tikzpicture}%
}
\vfill
\begin{block}{Claim boundary}
Residual contamination after security controls is the motivating scenario.
\end{block}
\end{frame}
\begin{frame}{Appendix: evaluation checklist (robustness culture)}
\footnotesize
\begin{enumerate}
\item Session-aware labels: avoid splitting rows inside a trajectory if that inflates scores.
\item Document how prototypes \(\bar{T}_H,\bar{T}_A\) were fit (full cohort vs.\ held-out); state explicitly in writing.
\item Report temperature \(T\) as calibration, not as a tuned hyperparameter unless a sweep is shown.
\item Separate \textbf{architecture} claims from \textbf{coverage} claims (hotel vs.\ airline balance at release).
\end{enumerate}
\end{frame}
\begin{frame}{Appendix: sim-to-real gap (explicit)}
\begin{itemize}
\item Kernels and generators reflect a \textbf{small labeled cohort} and a \textbf{browser-use style} agent class.
\item RL policies are trained in a \textbf{surrogate} market with engineered rewards and discretized prices.
\item Deployment would require legal review, fairness testing, and refreshed baselines at scale.
\end{itemize}
\end{frame}
\begin{frame}{Appendix: leakage surrogate (query-tax form)}
\small
\[
\mathrm{COI}_{\mathrm{leak}}(p,\tau') \approx f(\tau')\cdot c_{\mathrm{info}}
\]
\begin{block}{Reading}
\(f(\tau')\) is the weak agent score; \(c_{\mathrm{info}}\) is a minimal constant leakage proxy to expose the control channel. Revelation-style \(-\log \pi(p\mid\tau')\) is the natural upgrade.
\end{block}
\end{frame}
\begin{frame}{Appendix: robust pricing template (symbolic)}
\footnotesize
\[
\max_\pi\ \min_{Q\in\mathcal{U}_\epsilon(\hat{P}_N)} \mathbb{E}_{d\sim Q}\bigl[ R(p,d) - \lambda\,\mathrm{COI}_{\mathrm{leak}} - \eta\,\mathrm{UX} \bigr]
\]
\begin{alertblock}{Code-level substitute}
Inner min over a \textbf{finite grid} of \(\alpha_k\in[\alpha_0\pm\epsilon_\alpha]\) around the nominal generator mix, not a continuous adversary over all \(Q\) in the ball.
\end{alertblock}
\end{frame}
\begin{frame}{Appendix: why a Stackelberg game is a useful abstraction}
\footnotesize
\begin{columns}[T,onlytextwidth]
\column{0.52\textwidth}
\begin{itemize}
\item \textbf{Leader move}: the platform commits a quote via policy \(p_t=\pi(x_t)\).
\item \textbf{Follower move}: session behavior then reacts (click, continue, abandon, purchase).
\item This ordering matches real serving APIs: price is emitted before response is observed.
\item Repeating this local sequence gives a tractable leader-follower control model.
\end{itemize}
\column{0.44\textwidth}
\centering
\begin{tikzpicture}[
font=\scriptsize\sffamily,
box/.style={draw=PhantomInk,rounded corners=4pt,minimum width=3.45cm,minimum height=0.9cm,align=center},
arr/.style={-{Stealth[length=2.0mm]},thick,PhantomSlate}
]
\node[box,fill=PhantomCyan!14] (l) at (0,1.2) {Leader: pricing policy};
\node[box,fill=white] (f) at (0,-0.05) {Follower: session response};
\node[box,fill=PhantomIndigo!10] (u) at (0,-1.3) {State update \& next round};
\draw[arr] (l) -- node[right,font=\tiny] {quote} (f);
\draw[arr] (f) -- node[right,font=\tiny] {events} (u);
\draw[arr] (u.west) to[bend left=35] node[left,font=\tiny] {context} (l.west);
\end{tikzpicture}
\end{columns}
\begin{alertblock}{Boundary}
We do \textbf{not} claim a full market equilibrium. We claim a useful timing model for explainable policy updates under contamination.
\end{alertblock}
\end{frame}
\begin{frame}{Appendix: why Theorem 1 helps (without over-claiming)}
\footnotesize
\begin{columns}[T,onlytextwidth]
\column{0.48\textwidth}
\begin{block}{What the theorem gives us}
\begin{itemize}
\item A directional mechanism: independent recon pressure compresses COI.
\item A sanity check for reward design: leakage penalties should grow with recon likelihood.
\item A clean explanatory anchor for stakeholders and governance review.
\end{itemize}
\end{block}
\column{0.48\textwidth}
\begin{alertblock}{What the theorem does not claim}
\begin{itemize}
\item It is not a finite-sample forecast for every market.
\item It does not cover collusion or all adaptive adversaries.
\item It does not replace simulator evidence or offline policy validation.
\end{itemize}
\end{alertblock}
\end{columns}
\vspace{0.2em}
\begin{block}{Three evidence layers used in this thesis}
\textbf{Theorem 1} (mechanism direction) \(\rightarrow\) \textbf{simulator} (finite-regime quantification) \(\rightarrow\) \textbf{implementation} (local robust policy training).
\end{block}
\end{frame}
\begin{frame}{Appendix: composite strip (five plots, small multiples)}
\centering
{\footnotesize\itshape Same PDFs as the main talk, shrunk to scan the full panel at once.\par}
\vspace{0.25em}
\begin{columns}[T,onlytextwidth]
\column{0.19\textwidth}
\includegraphics[width=\linewidth,height=0.26\textheight,keepaspectratio]{final_focus_coi_by_alpha.pdf}
\column{0.19\textwidth}
\includegraphics[width=\linewidth,height=0.26\textheight,keepaspectratio]{final_focus_revenue_delta.pdf}
\column{0.19\textwidth}
\includegraphics[width=\linewidth,height=0.26\textheight,keepaspectratio]{final_focus_revenue_by_alpha.pdf}
\column{0.19\textwidth}
\includegraphics[width=\linewidth,height=0.26\textheight,keepaspectratio]{final_focus_risk_deltas.pdf}
\column{0.19\textwidth}
\includegraphics[width=\linewidth,height=0.26\textheight,keepaspectratio]{final_focus_coi_preservation_grid.pdf}
\end{columns}
\end{frame}

3
paper/defense/manim/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
__pycache__/
*.pyc
media/

View File

@@ -0,0 +1,176 @@
from __future__ import annotations
from typing import Iterable
import numpy as np
from manim import (
Arrow,
BLUE_D,
CurvedArrow,
DOWN,
DashedLine,
GREEN_C,
GREY_B,
LEFT,
Line,
MathTex,
Matrix,
RIGHT,
RoundedRectangle,
SurroundingRectangle,
Text,
UP,
VGroup,
config,
)
P_MIN = 80.0
P_MAX = 160.0
LIGHT_BG = "#F8F8F4"
INK = "#1E1E1E"
AXIS_INK = "#2C2C2C"
HIGHLIGHT = "#8F5F00"
config.background_color = LIGHT_BG
Text.set_default(color=INK)
MathTex.set_default(color=INK)
Line.set_default(color=AXIS_INK)
Arrow.set_default(color=AXIS_INK)
CurvedArrow.set_default(color=AXIS_INK)
DashedLine.set_default(color=AXIS_INK)
def normal_pdf(x: float, mu: float, sigma: float) -> float:
z = (x - mu) / sigma
return float(np.exp(-0.5 * z * z) / (sigma * np.sqrt(2.0 * np.pi)))
def scene_title(text: str) -> Text:
return Text(text, font_size=44, weight="BOLD", color=INK).to_edge(UP)
def card(
label: str,
color: str = BLUE_D,
width: float = 3.3,
height: float = 1.15,
font_size: float = 24,
) -> VGroup:
box = RoundedRectangle(corner_radius=0.15, width=width, height=height)
box.set_stroke(color=color, width=2.0)
box.set_fill(color=color, opacity=0.12)
text = Text(label, font_size=font_size).move_to(box.get_center())
return VGroup(box, text)
def to_matrix(
values: Iterable[Iterable[float]],
title: str,
color: str,
header_buff: float = 0.28,
fmt: str = ".2f",
) -> VGroup:
mat = Matrix(
[[f"{v:{fmt}}" for v in row] for row in values], h_buff=1.15, v_buff=0.75
)
header = Text(title, font_size=25, weight="BOLD", color=color).next_to(
mat, UP, buff=header_buff
)
frame = SurroundingRectangle(mat, color=color, buff=0.2)
return VGroup(header, frame, mat)
def rank_from_scale(scale: int) -> str:
clamped = max(1, min(scale, 10))
return "A" if clamped == 1 else str(clamped)
def actor_face_card(
rank: str,
role: str,
accent: str,
width: float = 1.6,
height: float = 2.25,
show_role: bool = True,
) -> VGroup:
frame = RoundedRectangle(corner_radius=0.1, width=width, height=height)
frame.set_stroke(color=AXIS_INK, width=2.0)
frame.set_fill(color="#FFFFFF", opacity=1.0)
top_rank = Text(rank, font_size=30, color=accent).move_to(
frame.get_corner(UP + LEFT) + RIGHT * 0.2 + DOWN * 0.22
)
bottom_rank = (
Text(rank, font_size=30, color=accent)
.rotate(np.pi)
.move_to(frame.get_corner(DOWN + RIGHT) + LEFT * 0.2 + UP * 0.22)
)
center_rank = Text(rank, font_size=56, weight="BOLD", color=accent).move_to(
frame.get_center() + UP * 0.03
)
parts = [frame, top_rank, bottom_rank, center_rank]
if show_role:
role_label = Text(role, font_size=18, color=GREY_B).next_to(
frame, DOWN, buff=0.08
)
parts.append(role_label)
return VGroup(*parts)
def product_suit_card(
suit: str,
scale: int,
accent: str,
width: float = 1.86,
height: float = 1.04,
show_label: bool = False,
) -> tuple[VGroup, Text]:
frame = RoundedRectangle(corner_radius=0.08, width=width, height=height)
frame.set_stroke(color=AXIS_INK, width=2.0)
frame.set_fill(color="#FFFFFF", opacity=1.0)
suit_left = Text(suit, font_size=28, color=accent).move_to(
frame.get_left() + RIGHT * 0.22
)
suit_right = Text(suit, font_size=28, color=accent).move_to(
frame.get_right() + LEFT * 0.22
)
scale_text = Text(
rank_from_scale(scale),
font_size=40,
weight="BOLD",
color=accent,
).move_to(frame.get_center())
parts = [frame, suit_left, suit_right, scale_text]
if show_label:
scale_label = Text("scale", font_size=14, color=GREY_B).next_to(
frame, DOWN, buff=0.04
)
parts.append(scale_label)
return VGroup(*parts), scale_text
def private_valuation_card(value: int, show_label: bool = False) -> VGroup:
frame = RoundedRectangle(corner_radius=0.08, width=1.86, height=1.04)
frame.set_stroke(color=AXIS_INK, width=2.0)
frame.set_fill(color="#FFFFFF", opacity=1.0)
rank = Text(
rank_from_scale(value), font_size=40, weight="BOLD", color=GREEN_C
).move_to(frame.get_center())
left_tag = Text("v", font_size=28, color=INK).move_to(
frame.get_left() + RIGHT * 0.22
)
right_tag = Text("*", font_size=28, color=INK).move_to(
frame.get_right() + LEFT * 0.22
)
parts = [frame, left_tag, right_tag, rank]
if show_label:
title = Text("private value", font_size=14, color=GREY_B).next_to(
frame, DOWN, buff=0.04
)
parts.append(title)
return VGroup(*parts)

View File

@@ -0,0 +1,23 @@
"""Manim entry module only.
Scene implementations are in scenes/main.py and scenes/appendix.py. Manim names
output folders after the file you pass to the CLI; pointing everything at this
file keeps all MP4s under media/videos/defense/ instead of splitting by source file.
"""
from __future__ import annotations
import importlib
from manim import Scene
_modname = __name__
for _mod in ("scenes.main", "scenes.appendix"):
m = importlib.import_module(_mod)
for _name, _val in list(vars(m).items()):
if _name.startswith("_"):
continue
if isinstance(_val, type) and issubclass(_val, Scene) and _val is not Scene:
_val.__module__ = _modname
globals()[_name] = _val

View File

@@ -0,0 +1,14 @@
# One scene name per line; order matches `python src/render.py --group final-full`.
# Used by scripts/ffmpeg_concat_defense.sh after rendering.
DefenseOpening
CardMarketAnalogyScene
COIFirstPrinciplesScene
COIOrderStatisticProofScene
BehaviorKernelConstructionScene
SeparabilitySignalScene
ContaminationGeneratorScene
RewardAndLeakageScene
StackelbergAmbiguityScene
RobustControlScene
SystemLoopScene
ObjectiveAndResultsScene

View File

@@ -0,0 +1,47 @@
{
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"name": "manim",
"projectType": "application",
"sourceRoot": "paper/defense/manim",
"targets": {
"render": {
"executor": "nx:run-commands",
"options": {
"command": "bash -c 'source ../.venv/bin/activate && PYTHONPATH=. python render.py'",
"cwd": "paper/defense/manim"
}
},
"render-all": {
"executor": "nx:run-commands",
"options": {
"command": "bash -c 'source ../.venv/bin/activate && PYTHONPATH=. python render.py --all'",
"cwd": "paper/defense/manim"
}
},
"render-full": {
"executor": "nx:run-commands",
"options": {
"command": "bash -c 'source ../.venv/bin/activate && PYTHONPATH=. python render.py --group final-full'",
"cwd": "paper/defense/manim"
}
},
"render-poster": {
"executor": "nx:run-commands",
"options": {
"command": "bash -c 'source ../.venv/bin/activate && PYTHONPATH=. python render.py --group poster'",
"cwd": "paper/defense/manim"
}
},
"render-appendix": {
"executor": "nx:run-commands",
"options": {
"command": "bash -c 'source ../.venv/bin/activate && PYTHONPATH=. python render.py --group behavior-appendix && PYTHONPATH=. python render.py --group coi-appendix'",
"cwd": "paper/defense/manim"
}
}
},
"tags": [
"scope:presentation",
"type:manim"
]
}

View File

@@ -0,0 +1,176 @@
from __future__ import annotations
import argparse
import os
import subprocess
import sys
from pathlib import Path
from scenes.appendix import BEHAVIOR_SCENES, COI_SCENES
from scenes.main import POSTER_SCENES, SCENE_ORDER as MAIN_SCENES
# ---------------------------------------------------------------------------
# Batch render: groups are just ordered lists of scene class names.
# Every scene is rendered via defense.py so outputs stay in media/videos/defense/.
# Scene code itself lives in scenes/main.py and scenes/appendix.py.
# ---------------------------------------------------------------------------
def _ordered_unique(items: list[str]) -> list[str]:
seen: set[str] = set()
return [item for item in items if not (item in seen or seen.add(item))]
FINAL_CORE = [
"DefenseOpening",
"CardMarketAnalogyScene",
"COIFirstPrinciplesScene",
"COIOrderStatisticProofScene",
"BehaviorKernelConstructionScene",
"SeparabilitySignalScene",
"ContaminationGeneratorScene",
"RewardAndLeakageScene",
"StackelbergAmbiguityScene",
"RobustControlScene",
"SystemLoopScene",
"ObjectiveAndResultsScene",
]
SCENE_GROUPS: dict[str, list[str]] = {
"poster": list(POSTER_SCENES),
"final-core": FINAL_CORE,
"final-full": list(MAIN_SCENES),
"behavior-appendix": list(BEHAVIOR_SCENES),
"coi-appendix": list(COI_SCENES),
}
SCENE_GROUPS["all"] = _ordered_unique(
[
*SCENE_GROUPS["final-full"],
*SCENE_GROUPS["poster"],
*SCENE_GROUPS["behavior-appendix"],
*SCENE_GROUPS["coi-appendix"],
]
)
ENTRY = "defense.py"
SCENE_TO_FILE: dict[str, str] = {name: ENTRY for name in SCENE_GROUPS["all"]}
DEFAULT_GROUP = "final-core"
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description=(
"Batch-render scenes. Code: scenes/main.py + scenes/appendix.py. "
"Manim entry: defense.py. Output: media/videos/defense/<quality>/"
)
)
parser.add_argument(
"--quality",
default="qm",
choices=["ql", "qm", "qh", "qk"],
help="Manim quality preset",
)
selection = parser.add_mutually_exclusive_group()
selection.add_argument(
"--scene",
action="append",
dest="scenes",
help="Scene name; repeat to render many",
)
selection.add_argument(
"--group",
choices=sorted(SCENE_GROUPS.keys()),
default=DEFAULT_GROUP,
help=f"Named list of scenes (default: {DEFAULT_GROUP})",
)
selection.add_argument("--all", action="store_true", help="Render every scene")
parser.add_argument(
"--media-dir",
default="media",
help="Relative to this folder (default: media)",
)
parser.add_argument("--preview", action="store_true", help="Open each video")
parser.add_argument("--list", action="store_true", help="Print groups and exit")
return parser.parse_args()
def validate_requested(requested: list[str]) -> list[str]:
missing = [name for name in requested if name not in SCENE_TO_FILE]
if missing:
choices = ", ".join(SCENE_TO_FILE.keys())
raise ValueError(
f"Unknown scenes: {', '.join(missing)}\nAvailable choices: {choices}"
)
return requested
def resolve_scenes(args: argparse.Namespace) -> list[str]:
if args.all:
return list(SCENE_GROUPS["all"])
if args.scenes:
return validate_requested(args.scenes)
return list(SCENE_GROUPS[args.group])
def run_manim(
scene_file: Path,
scene_name: str,
quality: str,
preview: bool,
working_dir: Path,
media_dir: str,
pythonpath: str,
) -> None:
env = os.environ.copy()
prev = env.get("PYTHONPATH")
env["PYTHONPATH"] = pythonpath if not prev else f"{pythonpath}:{prev}"
cmd = [sys.executable, "-m", "manim"]
if preview:
cmd.append("-p")
cmd.extend(["--media_dir", media_dir])
cmd.extend([f"-{quality}", str(scene_file), scene_name])
subprocess.run(cmd, cwd=working_dir, check=True, env=env)
def main() -> int:
args = parse_args()
if args.list:
for group_name in sorted(SCENE_GROUPS):
print(f"[{group_name}]")
for scene in SCENE_GROUPS[group_name]:
print(f" {scene}")
return 0
root = Path(__file__).resolve().parent
py_path = str(root)
names = resolve_scenes(args)
try:
for scene_name in names:
scene_file = root / SCENE_TO_FILE[scene_name]
run_manim(
scene_file=scene_file,
scene_name=scene_name,
quality=args.quality,
preview=args.preview,
working_dir=root,
media_dir=args.media_dir,
pythonpath=py_path,
)
except FileNotFoundError:
print("manim not found.", file=sys.stderr)
return 2
except ValueError as exc:
print(str(exc), file=sys.stderr)
return 2
except subprocess.CalledProcessError as exc:
return exc.returncode
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,89 @@
#!/usr/bin/env bash
# Render thesis-defense Manim clips. Run from anywhere (script cd's to its dir).
#
# ./render_defense # main reel: final-full, medium quality
# ./render_defense --quality qh # high quality for recording
# ./render_defense core # shorter committee cut (final-core)
# ./render_defense all # everything: main + poster + both appendices
# ./render_defense appendix # behavior + COI appendix only
# ./render_defense poster
# ./render_defense list
# ./render_defense --scene DefenseOpening --scene CardMarketAnalogyScene
#
# Env: MANIM_PYTHON=/path/to/python overrides auto-detected venv.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")" && pwd)"
cd "$ROOT"
if [[ -n "${MANIM_PYTHON:-}" ]]; then
PY="$MANIM_PYTHON"
elif [[ -x "$ROOT/../.venv/bin/python" ]]; then
PY="$ROOT/../.venv/bin/python"
else
PY="$(command -v python3 2>/dev/null || command -v python)"
fi
if [[ ! -x "$PY" ]] && ! command -v "$PY" &>/dev/null; then
echo "No Python found. Set MANIM_PYTHON or create paper/defense/.venv" >&2
exit 1
fi
export PYTHONPATH="$ROOT"
run() {
"$PY" "$ROOT/render.py" "$@"
}
CMD=full
case "${1-}" in
full|core|all|appendix|poster|list|help|-h|--help)
CMD="$1"
shift
;;
esac
case "$CMD" in
help|-h|--help)
cat <<'EOF'
Render thesis-defense Manim clips (cd to paper/defense/manim is automatic).
./render_defense main reel (final-full), default quality qm
./render_defense --quality qh same, high quality for recording
./render_defense core shorter cut (final-core)
./render_defense all main + poster + both appendices
./render_defense appendix behavior-appendix + coi-appendix
./render_defense poster
./render_defense list scene names and source files
./render_defense --scene Name [--scene Name2 ...]
Env MANIM_PYTHON overrides Python (default: ../.venv/bin/python next to this dir).
EOF
exit 0
;;
list)
run --list "$@"
exit 0
;;
full)
run --group final-full "$@"
;;
core)
run --group final-core "$@"
;;
all)
run --all "$@"
;;
appendix)
run --group behavior-appendix "$@"
run --group coi-appendix "$@"
;;
poster)
run --group poster "$@"
;;
*)
echo "Unknown command: $CMD" >&2
exit 1
;;
esac

View File

View File

@@ -0,0 +1,670 @@
from __future__ import annotations
import numpy as np
from manim import *
from common import AXIS_INK, HIGHLIGHT, INK, P_MAX, P_MIN, card, normal_pdf, scene_title, to_matrix
class LogsToKernelsScene(Scene):
def construct(self):
title = scene_title("From Event Logs to Transition Kernels")
self.play(Write(title))
# 1. Logs
log_lines = VGroup(
Text('{"session": "H1", "event": "start"}', font="monospace", font_size=16),
Text('{"session": "A1", "event": "start"}', font="monospace", font_size=16),
Text('{"session": "H1", "event": "view"}', font="monospace", font_size=16),
Text('{"session": "A1", "event": "view"}', font="monospace", font_size=16),
Text(
'{"session": "H1", "event": "detail"}', font="monospace", font_size=16
),
Text(
'{"session": "A1", "event": "detail"}', font="monospace", font_size=16
),
Text('{"session": "H1", "event": "cart"}', font="monospace", font_size=16),
Text('{"session": "A1", "event": "view"}', font="monospace", font_size=16),
Text('{"session": "H1", "event": "buy"}', font="monospace", font_size=16),
Text(
'{"session": "A1", "event": "detail"}', font="monospace", font_size=16
),
).arrange(DOWN, aligned_edge=LEFT, buff=0.1)
log_lines.to_edge(LEFT, buff=1.0).shift(UP * 0.5)
self.play(
LaggedStart(
*[FadeIn(line, shift=UP * 0.1) for line in log_lines], lag_ratio=0.1
)
)
self.wait(0.5)
# 2. Nodes in a grid
def create_node(text, color):
circ = Circle(radius=0.4, color=color, fill_opacity=0.2)
lbl = Text(text, font_size=14).move_to(circ)
return VGroup(circ, lbl)
h_states = ["start", "view", "detail", "cart", "buy"]
a_states = ["start", "view", "detail", "view", "detail"]
h_nodes = VGroup(*[create_node(s, BLUE_D) for s in h_states]).arrange(
RIGHT, buff=0.5
)
a_nodes = VGroup(*[create_node(s, RED_C) for s in a_states]).arrange(
RIGHT, buff=0.5
)
trajectories = VGroup(h_nodes, a_nodes).arrange(DOWN, buff=1.0)
trajectories.to_edge(RIGHT, buff=1.0).shift(UP * 0.5)
h_label = Text("Human Trajectory", font_size=18, color=BLUE_D).next_to(
h_nodes, UP
)
a_label = Text("Agent Trajectory", font_size=18, color=RED_C).next_to(
a_nodes, UP
)
self.play(
ReplacementTransform(log_lines[0::2], h_nodes),
ReplacementTransform(log_lines[1::2], a_nodes),
FadeIn(h_label),
FadeIn(a_label),
)
# Add connecting lines
h_lines = VGroup(
*[
Line(h_nodes[i].get_right(), h_nodes[i + 1].get_left(), color=BLUE_D)
for i in range(len(h_nodes) - 1)
]
)
a_lines = VGroup(
*[
Line(a_nodes[i].get_right(), a_nodes[i + 1].get_left(), color=RED_C)
for i in range(len(a_nodes) - 1)
]
)
self.play(Create(h_lines), Create(a_lines))
self.wait(1)
# 3. Counts to Kernel
mle_text = MathTex(
r"\hat P(s'\mid s) = \frac{N(s,s')}{\sum_k N(s,k)}",
font_size=36,
color=HIGHLIGHT,
)
mle_text.next_to(trajectories, DOWN, buff=0.8)
self.play(Write(mle_text))
counts = to_matrix(
[
[0, 8, 0, 0],
[0, 2, 5, 1],
[0, 3, 2, 4],
[0, 1, 0, 6],
],
"Count Matrix N",
color=BLUE_D,
fmt=".0f",
)
probs = to_matrix(
[
[0.00, 1.00, 0.00, 0.00],
[0.00, 0.25, 0.62, 0.13],
[0.00, 0.33, 0.22, 0.45],
[0.00, 0.14, 0.00, 0.86],
],
"Kernel T",
color=GREEN_C,
)
mats = VGroup(counts, probs).arrange(RIGHT, buff=1.5).scale(0.65)
arrow = Arrow(counts.get_right(), probs.get_left(), buff=0.2)
arrow_lbl = MathTex(
r"\text{normalize}", font_size=18, color=GREY_B
).next_to(arrow, UP)
# clear top half to make space if needed
self.play(
FadeOut(h_nodes),
FadeOut(a_nodes),
FadeOut(h_lines),
FadeOut(a_lines),
FadeOut(h_label),
FadeOut(a_label),
mle_text.animate.to_edge(UP, buff=1.5).set_x(0),
)
mats.next_to(mle_text, DOWN, buff=0.5)
arrow.move_to((counts.get_right() + probs.get_left()) / 2)
arrow_lbl.next_to(arrow, UP)
self.play(FadeIn(counts, shift=UP * 0.2))
self.play(GrowArrow(arrow), FadeIn(arrow_lbl))
self.play(FadeIn(probs, shift=UP * 0.2))
self.wait(1)
class KLSeparabilityAndSignificanceScene(Scene):
def construct(self):
title = scene_title("Behavioral Separability & Significance")
self.play(Write(title))
human_mat = to_matrix(
[
[0.05, 0.70, 0.20, 0.05],
[0.05, 0.20, 0.60, 0.15],
[0.10, 0.25, 0.30, 0.35],
[0.00, 0.00, 0.00, 1.00],
],
"Human Centroid T_H",
BLUE_D,
).scale(0.7)
agent_mat = to_matrix(
[
[0.03, 0.82, 0.12, 0.03],
[0.06, 0.55, 0.21, 0.18],
[0.08, 0.48, 0.14, 0.30],
[0.00, 0.00, 0.00, 1.00],
],
"Agent Centroid T_A",
RED_C,
).scale(0.7)
centroids = VGroup(human_mat, agent_mat).arrange(RIGHT, buff=1.0)
centroids.next_to(title, DOWN, buff=0.5)
self.play(FadeIn(centroids, shift=DOWN * 0.2))
# Trajectory
t_prime = MathTex(r"\hat T'", font_size=36, color=HIGHLIGHT)
d_h = MathTex(r"\Delta_H = D_{KL}(\hat T' \parallel \bar T_H)", font_size=32)
d_a = MathTex(r"\Delta_A = D_{KL}(\hat T' \parallel \bar T_A)", font_size=32)
gap = MathTex(r"g = \Delta_H - \Delta_A", font_size=36, color=HIGHLIGHT)
eqs = VGroup(t_prime, d_h, d_a, gap).arrange(DOWN, buff=0.2)
eqs.to_edge(LEFT, buff=1.0).shift(DOWN * 1.0)
self.play(Write(eqs))
# Distributions
axis = (
Axes(
x_range=[-8, 8, 2],
y_range=[0, 0.2, 0.05],
x_length=6,
y_length=3,
tips=False,
axis_config={"color": AXIS_INK, "stroke_width": 2},
)
.to_edge(RIGHT, buff=1.0)
.shift(DOWN * 1.0)
)
mu_h, sig_h = -3.5, 2.0
mu_a, sig_a = 3.5, 2.0
h_curve = axis.plot(
lambda x: normal_pdf(x, mu_h, sig_h), color=BLUE_D, stroke_width=4
)
a_curve = axis.plot(
lambda x: normal_pdf(x, mu_a, sig_a), color=RED_C, stroke_width=4
)
h_lbl = (
Text("Human", color=BLUE_D, font_size=20)
.next_to(h_curve, UP, buff=-0.5)
.shift(LEFT * 1)
)
a_lbl = (
Text("Agent", color=RED_C, font_size=20)
.next_to(a_curve, UP, buff=-0.5)
.shift(RIGHT * 1)
)
boundary = DashedLine(axis.c2p(0, 0), axis.c2p(0, 0.18), color=GREY_B)
self.play(FadeIn(axis))
self.play(Create(h_curve), Create(a_curve))
self.play(FadeIn(h_lbl), FadeIn(a_lbl), FadeIn(boundary))
sig_text = MathTex(
r"p<10^{-3}\ \text{(Mann--Whitney)}", font_size=24, color=GREEN_C
)
sig_text.next_to(axis, DOWN, buff=0.3)
self.play(Write(sig_text))
self.wait(1)
class TrajectorySamplingScene(Scene):
def construct(self):
title = scene_title("Generative Trajectory Sampling")
self.play(Write(title))
agent_mat = to_matrix(
[
[0.00, 0.80, 0.20, 0.00, 0.00],
[0.00, 0.30, 0.50, 0.20, 0.00],
[0.00, 0.40, 0.30, 0.30, 0.00],
[0.00, 0.10, 0.10, 0.10, 0.70],
[0.00, 0.00, 0.00, 0.00, 1.00],
],
"Agent Kernel T_A",
RED_C,
).scale(0.6)
agent_mat.to_edge(LEFT, buff=1.0)
self.play(FadeIn(agent_mat))
states = ["Start", "View", "Detail", "Cart", "Buy"]
def create_node(text):
circ = Circle(radius=0.4, color=AXIS_INK, fill_opacity=0.1)
lbl = Text(text, font_size=16).move_to(circ)
return VGroup(circ, lbl)
nodes = VGroup(*[create_node(s) for s in states]).arrange(RIGHT, buff=0.6)
nodes.to_edge(RIGHT, buff=0.5).shift(UP * 1.0)
self.play(FadeIn(nodes))
# Output trajectory string
traj_label = (
Text("Sampled Trajectory:", font_size=24, color=HIGHLIGHT)
.to_edge(DOWN)
.shift(UP * 1.5 + LEFT * 1)
)
self.play(FadeIn(traj_label))
walker = Dot(color=HIGHLIGHT, radius=0.15)
walker.move_to(nodes[0].get_top() + UP * 0.2)
self.play(FadeIn(walker))
# Simulation
path = [0, 1, 2, 1, 2] # Start -> View -> Detail -> View -> Detail
# We will build the string
current_traj = VGroup(Text("Start", font_size=24, color=RED_C)).next_to(
traj_label, RIGHT
)
self.play(FadeIn(current_traj))
for i in range(len(path) - 1):
curr_state = path[i]
next_state = path[i + 1]
# highlight row
mat_core = agent_mat[2] # the matrix itself
# Using get_rows() which is standard in Mobject Matrix
row_entries = mat_core.get_rows()[curr_state]
row_rect = SurroundingRectangle(row_entries, color=HIGHLIGHT, buff=0.1)
self.play(Create(row_rect), run_time=0.5)
# move walker
arc = CurvedArrow(
walker.get_center(),
nodes[next_state].get_top() + UP * 0.2,
angle=-TAU / 4,
)
self.play(MoveAlongPath(walker, arc), run_time=1.0)
# Update string
arrow_str = MathTex(r"\rightarrow", font_size=24).next_to(
current_traj, RIGHT
)
next_str = Text(states[next_state], font_size=24, color=RED_C).next_to(
arrow_str, RIGHT
)
self.play(
FadeIn(arrow_str), FadeIn(next_str), FadeOut(row_rect), run_time=0.5
)
current_traj.add(arrow_str, next_str)
self.wait(1)
class KroneckerExpansionScene(Scene):
def construct(self):
title = scene_title("State-Space Expansion")
self.play(Write(title))
t_mat = to_matrix([[0.2, 0.8], [0.4, 0.6]], "Behavior T", BLUE_D)
d_mat = to_matrix([[0.9, 0.1], [0.5, 0.5]], "Demand D", RED_C)
kron_sym = MathTex(r"\otimes", font_size=60)
eq_sym = MathTex(r"=", font_size=60)
lhs = VGroup(t_mat, kron_sym, d_mat).arrange(RIGHT, buff=0.5)
lhs.next_to(title, DOWN, buff=1.0)
self.play(FadeIn(t_mat), FadeIn(d_mat), Write(kron_sym))
self.wait(1)
self.play(lhs.animate.scale(0.6).to_edge(LEFT, buff=0.5))
# Show expanded
# T tensor D
expanded = to_matrix(
[
[0.18, 0.02, 0.72, 0.08],
[0.10, 0.10, 0.40, 0.40],
[0.36, 0.04, 0.54, 0.06],
[0.20, 0.20, 0.30, 0.30],
],
r"Expanded P = T \otimes D",
HIGHLIGHT,
).scale(0.6)
eq_sym.next_to(lhs, RIGHT, buff=0.5)
expanded.next_to(eq_sym, RIGHT, buff=0.5)
self.play(Write(eq_sym), FadeIn(expanded, shift=LEFT * 0.5))
# Highlight a block
# the top right block (0.8 * D)
# rows 0,1 cols 2,3
# In expanded:
# row 0: 0, 1, 2, 3
# row 1: 4, 5, 6, 7
t_entries = t_mat[2].get_entries()
if len(t_entries) >= 2:
rect_T = SurroundingRectangle(
t_entries[1], color=HIGHLIGHT
) # T[0,1] is 0.8
else:
rect_T = VGroup()
exp_entries = expanded[2].get_entries()
if len(exp_entries) >= 8:
block_entries = VGroup(
exp_entries[2], exp_entries[3], exp_entries[6], exp_entries[7]
)
rect_block = SurroundingRectangle(block_entries, color=HIGHLIGHT)
else:
rect_block = VGroup()
desc = MathTex(
r"P(s', d' \mid s, d)=T(s'\mid s)\,D(d'\mid d, s')",
font_size=26,
color=HIGHLIGHT,
)
desc.next_to(expanded, DOWN, buff=0.5)
if len(t_entries) >= 2 and len(exp_entries) >= 8:
self.play(Create(rect_T), Create(rect_block))
self.play(Write(desc))
self.wait(1)
class SamplingAndReservationScene(Scene):
def construct(self):
title = scene_title("Pricing Policy & Reservation Price")
self.play(Write(title))
# 1. The setup
setup = VGroup(
MathTex(r"p_i \sim \pi(p \mid \tau)", font_size=44),
MathTex(
r"\underline p = \text{reservation price}", font_size=38, color=ORANGE
),
).arrange(DOWN, aligned_edge=LEFT, buff=0.3)
setup.to_edge(LEFT, buff=1.0).shift(UP * 1.0)
self.play(Write(setup[0]))
self.play(Write(setup[1]))
# 2. Number line sampling
number_line = NumberLine(
x_range=[P_MIN, P_MAX, 10],
length=9.8,
color=AXIS_INK,
include_numbers=True,
decimal_number_config={"num_decimal_places": 0, "color": INK},
).shift(DOWN * 1.0)
self.play(FadeIn(number_line))
# Floor marker
floor_marker = Line(
number_line.n2p(P_MIN),
number_line.n2p(P_MIN) + UP * 0.85,
color=ORANGE,
stroke_width=5,
)
floor_label = MathTex(r"\underline p", color=ORANGE).next_to(
floor_marker, UP, buff=0.05
)
self.play(Create(floor_marker), FadeIn(floor_label))
# Animate sampling
rng = np.random.default_rng(42)
n_samples = 5
draws = np.sort(rng.beta(2.5, 2.0, size=n_samples) * (P_MAX - P_MIN) + P_MIN)
dots = VGroup()
for i, val in enumerate(draws):
# Show drawing process
temp_dot = Dot(number_line.n2p(120), radius=0.08, color=BLUE_D).shift(
UP * 1.5
)
self.play(FadeIn(temp_dot), run_time=0.2)
final_pos = number_line.n2p(float(val))
self.play(temp_dot.animate.move_to(final_pos), run_time=0.3)
dots.add(temp_dot)
self.wait(0.5)
# Highlight minimum
min_dot = dots[0]
min_highlight = Circle(radius=0.15, color=RED_C).move_to(min_dot)
min_tag = MathTex(r"p_{(1)}", color=RED_C).next_to(min_highlight, UP, buff=0.1)
self.play(Create(min_highlight), Write(min_tag))
desc = MathTex(
r"\text{realized price }p_{(1)}=\min\{p_1,\ldots,p_N\}",
font_size=26,
color=GREY_B,
).to_edge(DOWN)
self.play(FadeIn(desc, shift=UP * 0.2))
self.wait(1.5)
class COIDistributionScene(Scene):
def construct(self):
title = scene_title("Cost of Information (COI)")
self.play(Write(title))
# COI definition
coi_def = MathTex(
r"\mathrm{COI} = \mathbb{E}[P] - \underline p",
font_size=46,
color=HIGHLIGHT,
).next_to(title, DOWN, buff=0.5)
self.play(Write(coi_def))
# Distribution plot
floor_x = 86.0
mean_x = 116.0
axes = Axes(
x_range=[80, 160, 10],
y_range=[0.0, 0.04, 0.01],
x_length=8.0,
y_length=4.0,
tips=False,
axis_config={"stroke_width": 2, "color": AXIS_INK},
).shift(DOWN * 0.5)
density = axes.plot(
lambda x: normal_pdf(x, mean_x, 12.0),
x_range=[80, 160],
color=BLUE_D,
stroke_width=6,
)
area = axes.get_area(density, x_range=[80, 160], color=BLUE_D, opacity=0.2)
self.play(FadeIn(axes))
self.play(Create(density), FadeIn(area))
# Markers
floor_line = DashedLine(
axes.c2p(floor_x, 0.0),
axes.c2p(floor_x, 0.038),
color=ORANGE,
stroke_width=4,
)
mean_line = DashedLine(
axes.c2p(mean_x, 0.0),
axes.c2p(mean_x, 0.038),
color=GREEN_C,
stroke_width=4,
)
floor_tag = MathTex(r"\underline p", color=ORANGE).next_to(
floor_line, UP, buff=0.1
)
mean_tag = MathTex(r"\mathbb{E}[P]", color=GREEN_C).next_to(
mean_line, UP, buff=0.1
)
self.play(Create(floor_line), Write(floor_tag))
self.play(Create(mean_line), Write(mean_tag))
# COI span
coi_arrow = DoubleArrow(
axes.c2p(floor_x, 0.02), axes.c2p(mean_x, 0.02), color=HIGHLIGHT, buff=0
)
coi_label = Text("COI", font_size=24, color=HIGHLIGHT).next_to(
coi_arrow, UP, buff=0.1
)
self.play(GrowFromCenter(coi_arrow), Write(coi_label))
desc = MathTex(
r"\mathrm{COI}=\mathbb{E}[P]-\underline p",
font_size=28,
color=GREY_B,
).to_edge(DOWN)
self.play(FadeIn(desc, shift=UP * 0.2))
self.wait(1.5)
class COIErosionMathScene(Scene):
def construct(self):
title = scene_title("Mathematical Proof of COI Erosion")
self.play(Write(title))
# Step 1: Expected value of minimum
eq1 = MathTex(
r"\mathbb{E}[p_{(1)}] = \underline p + \int_{\underline p}^{\bar p} \mathbb{P}(p_{(1)} > t) dt",
font_size=36,
)
# Step 2: Probability of minimum > t
eq2 = MathTex(
r"\mathbb{P}(p_{(1)} > t) = \mathbb{P}(p_1 > t) \times \dots \times \mathbb{P}(p_N > t)",
font_size=36,
)
# Step 3: Assuming i.i.d
eq3 = MathTex(r"= [1 - F_\pi(t)]^N", font_size=36, color=HIGHLIGHT)
# Step 4: Substitute back
eq4 = MathTex(
r"\mathbb{E}[p_{(1)}] = \underline p + \int_{\underline p}^{\bar p} [1 - F_\pi(t)]^N dt",
font_size=36,
)
# Step 5: Limit as N -> inf
eq5_pt1 = MathTex(
r"\text{Since } [1 - F_\pi(t)] < 1 \text{ for } t > \underline p:",
font_size=32,
color=GREY_B,
)
eq5_pt2 = MathTex(
r"\lim_{N \to \infty} \mathbb{E}[p_{(1)}] = \underline p",
font_size=42,
color=RED_C,
)
eq6 = MathTex(
r"\lim_{N \to \infty} \mathrm{COI} = 0", font_size=46, color=HIGHLIGHT
)
group = VGroup(eq1, eq2, eq3, eq4, eq5_pt1, eq5_pt2, eq6).arrange(
DOWN, aligned_edge=LEFT, buff=0.4
)
group.next_to(title, DOWN, buff=0.5).shift(RIGHT * 1.5)
# We want eq3 to be right after eq2
eq3.next_to(eq2, RIGHT, buff=0.2)
# Re-arrange carefully
step1 = eq1.copy().to_edge(LEFT, buff=1.0).shift(UP * 1.5)
step2 = (
VGroup(eq2.copy(), eq3.copy())
.arrange(RIGHT, buff=0.2)
.next_to(step1, DOWN, aligned_edge=LEFT, buff=0.5)
)
step3 = eq4.copy().next_to(step2, DOWN, aligned_edge=LEFT, buff=0.5)
step4_group = (
VGroup(eq5_pt1.copy(), eq5_pt2.copy())
.arrange(DOWN, aligned_edge=LEFT, buff=0.2)
.next_to(step3, DOWN, aligned_edge=LEFT, buff=0.5)
)
step5 = eq6.copy().next_to(step4_group, DOWN, buff=0.6).match_x(title)
# Animate
self.play(Write(step1))
self.wait(0.5)
self.play(Write(step2[0]))
self.play(Write(step2[1]))
self.wait(0.5)
self.play(Write(step3))
self.wait(0.5)
self.play(Write(step4_group[0]))
self.play(Write(step4_group[1]))
self.wait(0.5)
# Put a box around the final conclusion
box = SurroundingRectangle(step5, color=HIGHLIGHT, buff=0.2)
self.play(Write(step5), Create(box))
desc = MathTex(
r"N\to\infty\ \Rightarrow\ \mathrm{COI}\to 0",
font_size=28,
color=GREY_B,
).to_edge(DOWN)
self.play(FadeIn(desc, shift=UP * 0.2))
self.wait(2)
BEHAVIOR_SCENES = [
"LogsToKernelsScene",
"KLSeparabilityAndSignificanceScene",
"TrajectorySamplingScene",
"KroneckerExpansionScene",
]
COI_SCENES = [
"SamplingAndReservationScene",
"COIDistributionScene",
"COIErosionMathScene",
]

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Concatenate rendered defense scenes (all under media/videos/defense/<quality>/).
# Usage from paper/defense/manim after: ./render_defense full --quality qh
# ./scripts/ffmpeg_concat_defense.sh qh
set -euo pipefail
QUALITY="${1:-qm}"
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
LIST="$(mktemp)"
trap 'rm -f "$LIST"' EXIT
DIR="$ROOT/media/videos/defense/$QUALITY"
while IFS= read -r line || [[ -n "$line" ]]; do
[[ "$line" =~ ^#.*$ || -z "${line// }" ]] && continue
name="$line"
f="$DIR/${name}.mp4"
if [[ ! -f "$f" ]]; then
echo "missing: $f" >&2
exit 1
fi
echo "file '$f'" >>"$LIST"
done <"$ROOT/defense_scene_order.txt"
OUT="$ROOT/media/defense_rehearsal_${QUALITY}.mp4"
ffmpeg -y -f concat -safe 0 -i "$LIST" -c copy "$OUT"
echo "wrote $OUT"

View File

@@ -120,8 +120,8 @@ case "$cmd" in
python3 - <<'PY'
from pathlib import Path
skip = {"node_modules", ".venv", "venv"}
exts = {".ts", ".py"}
skip = {"node_modules", ".venv", "venv", ".venv-ray"}
exts = {".ts", ".py", ".ipynb"}
total = 0
for path in Path(".").rglob("*"):
if not path.is_file() or path.suffix not in exts or any(part in skip for part in path.parts):