4
.gitignore
vendored
@@ -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/
|
||||
|
||||
10
Makefile
@@ -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
|
||||
|
||||
@@ -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
|
||||

|
||||
|
||||
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
|
After Width: | Height: | Size: 157 KiB |
309
docs/index.html
@@ -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,92 +243,87 @@
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<p class="tpu-credit">Powered by <span class="accent">Google</span> TPU Research Cloud.</p>
|
||||
</div>
|
||||
|
||||
<section class="hero teaser">
|
||||
<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>
|
||||
</div>
|
||||
</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 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>T̂<sub>H</sub></em> and <em>T̂<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>), Δ<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 Mann–Whitney 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>T̂<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> & 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] − <u>p</u></div>
|
||||
<p class="caption">Cost of Information — the expected premium dynamic pricing earns over the reservation price — 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–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–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 α removes ~90,140 in cohort revenue (p < 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 α ∈ [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">🤗</div>
|
||||
<div>
|
||||
<h4>WhoClickedIt — 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
@@ -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;
|
||||
}
|
||||
}
|
||||
9
docs/static/css/index.css
vendored
@@ -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
@@ -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
|
After Width: | Height: | Size: 25 KiB |
6
docs/static/images/banner.svg
vendored
@@ -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"> > 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̂' || T̄</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̂' || T̄</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 |
BIN
docs/static/images/favicon.ico
vendored
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 15 KiB |
87
docs/static/images/human.svg
vendored
Normal 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 |
51
paper/defense/NARRATIVE.md
Normal 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
@@ -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
0
paper/defense/defense.snm
Normal file
932
paper/defense/defense.tex
Normal 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}
|
||||
364
paper/defense/defense_appendix.tex
Normal 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
@@ -0,0 +1,3 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
media/
|
||||
176
paper/defense/manim/common.py
Normal 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)
|
||||
23
paper/defense/manim/defense.py
Normal 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
|
||||
14
paper/defense/manim/defense_scene_order.txt
Normal 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
|
||||
47
paper/defense/manim/project.json
Normal 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"
|
||||
]
|
||||
}
|
||||
176
paper/defense/manim/render.py
Normal 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())
|
||||
89
paper/defense/manim/render_defense
Executable 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
|
||||
0
paper/defense/manim/scenes/__init__.py
Normal file
670
paper/defense/manim/scenes/appendix.py
Normal 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",
|
||||
]
|
||||
1523
paper/defense/manim/scenes/main.py
Normal file
25
paper/defense/manim/scripts/ffmpeg_concat_defense.sh
Executable 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"
|
||||
@@ -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):
|
||||
|
||||