SabaPivot commited on
Commit
5baae82
·
verified ·
1 Parent(s): 274c04a

Update logbook: Reproduction: Distributed Direct Preference Optimization

Browse files
.serve.log ADDED
File without changes
README.md CHANGED
@@ -1,10 +1,22 @@
1
  ---
2
- title: Repro Distributed Direct Preference Optimization
3
- emoji: 🐢
4
- colorFrom: green
5
- colorTo: indigo
6
  sdk: static
7
  pinned: false
 
 
 
 
 
 
 
 
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
1
  ---
2
+ title: "Reproduction: Distributed Direct Preference Optimization"
3
+ emoji: 🎯
4
+ colorFrom: yellow
5
+ colorTo: red
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - trackio
10
+ - trackio-logbook
11
+ - open-experiment
12
+ - trackio
13
+ - open-reproductions
14
+ - icml2026
15
+ - icml2026-repro
16
+ - paper-ljNZyrAlaa
17
+ - arxiv:2605.20696
18
  ---
19
 
20
+ # Reproduction: Distributed Direct Preference Optimization
21
+
22
+ An open experiment logbook, published with [Trackio](https://github.com/gradio-app/trackio).
bucket-icon.svg ADDED
index.html CHANGED
@@ -1,19 +1,84 @@
1
  <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
  <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Reproduction: Distributed Direct Preference Optimization</title>
7
+ <link rel="stylesheet" href="./logbook.css" />
8
+ </head>
9
+ <body>
10
+ <div id="app">
11
+ <aside id="sidebar">
12
+ <div id="book-head">
13
+ <img id="book-wordmark" src="./trackio-wordmark-dark.png" alt="" />
14
+ <div id="book-title" class="sr-only">Logbook</div>
15
+ </div>
16
+ <nav id="tree"></nav>
17
+ <div id="sidebar-foot" hidden>
18
+ <button id="connect-btn" type="button">
19
+ <span class="ico">ⓘ</span> Collaborate with your agent
20
+ </button>
21
+ </div>
22
+ </aside>
23
+ <main id="content">
24
+ <nav id="view-tabs" aria-label="Logbook views">
25
+ <a data-view="code" href="#/view/code/index">
26
+ <svg viewBox="0 0 24 24" aria-hidden="true">
27
+ <path d="m18 16 4-4-4-4" />
28
+ <path d="m6 8-4 4 4 4" />
29
+ <path d="m14.5 4-5 16" />
30
+ </svg>
31
+ <span>Logbook</span>
32
+ </a>
33
+ <a data-view="trace" href="#/view/trace">
34
+ <svg viewBox="0 0 24 24" aria-hidden="true">
35
+ <path d="M8 5h13" />
36
+ <path d="M13 12h8" />
37
+ <path d="M13 19h8" />
38
+ <path d="M3 10a2 2 0 0 0 2 2h3" />
39
+ <path d="M3 5v12a2 2 0 0 0 2 2h3" />
40
+ </svg>
41
+ <span>Traces</span>
42
+ </a>
43
+ <a data-view="workspace" href="#/view/workspace">
44
+ <svg viewBox="0 0 24 24" aria-hidden="true">
45
+ <path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />
46
+ </svg>
47
+ <span>Workspace</span>
48
+ </a>
49
+ </nav>
50
+ <header id="logbook-header">
51
+ <h1 id="logbook-title"></h1>
52
+ <div id="logbook-cli"></div>
53
+ </header>
54
+ <div id="page"></div>
55
+ </main>
56
+ </div>
57
+
58
+ <div id="modal" hidden>
59
+ <div class="modal-backdrop"></div>
60
+ <div class="modal-card" role="dialog" aria-modal="true">
61
+ <div class="modal-head">
62
+ <div class="modal-title">
63
+ <img class="modal-logo" src="./trackio-logo.png" alt="" />
64
+ Collaborate with your agent
65
+ </div>
66
+ <div class="modal-actions">
67
+ <button id="copy-agent" class="btn">Copy for agent</button>
68
+ <button id="modal-close" class="btn icon" aria-label="Close">×</button>
69
+ </div>
70
+ </div>
71
+ <div class="modal-body">
72
+ <p class="modal-intro">
73
+ Point your coding agent at this logbook. It reads a compact,
74
+ token-efficient version — and if you've given it write access to this
75
+ Space, it can add findings that sync back automatically.
76
+ </p>
77
+ <ol id="connect-steps"></ol>
78
+ </div>
79
+ </div>
80
+ </div>
81
+
82
+ <script src="./logbook.js"></script>
83
+ </body>
84
  </html>
logbook.css ADDED
@@ -0,0 +1,2142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --bg: #ffffff;
3
+ --paper: #ffffff;
4
+ --panel: #ffffff;
5
+ --ink: #1f2937;
6
+ --muted: #6b7280;
7
+ --line: #e5e7eb;
8
+ --accent: #f97316;
9
+ --accent-strong: #ea580c;
10
+ --accent-soft: #fff7ed;
11
+ --accent-line: rgba(249, 115, 22, 0.16);
12
+ --grid-line: rgba(31, 41, 55, 0.02);
13
+ --code-bg: #f3f4f6;
14
+ --radius: 12px;
15
+ --sidebar-width: 280px;
16
+ --content-gutter: 40px;
17
+ --serif: ui-serif, "Iowan Old Style", "Palatino Linotype", Georgia, serif;
18
+ --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial,
19
+ sans-serif;
20
+ --mono: "SFMono-Regular", "Cascadia Mono", "JetBrains Mono", Menlo, Consolas,
21
+ ui-monospace, monospace;
22
+ }
23
+
24
+ * {
25
+ box-sizing: border-box;
26
+ }
27
+
28
+ html,
29
+ body {
30
+ margin: 0;
31
+ padding: 0;
32
+ }
33
+
34
+ html {
35
+ scroll-behavior: smooth;
36
+ scrollbar-gutter: stable;
37
+ }
38
+
39
+ body {
40
+ background: var(--bg);
41
+ color: var(--ink);
42
+ font-family: var(--sans);
43
+ font-size: 13px;
44
+ line-height: 1.65;
45
+ -webkit-font-smoothing: antialiased;
46
+ }
47
+
48
+ #app {
49
+ display: flex;
50
+ min-height: 100vh;
51
+ }
52
+
53
+ body[data-view="trace"] #sidebar-foot,
54
+ body[data-view="workspace"] #sidebar-foot {
55
+ display: none;
56
+ }
57
+
58
+ /* ---- sidebar (composition-book cover) ---- */
59
+ #sidebar {
60
+ width: var(--sidebar-width);
61
+ flex: 0 0 var(--sidebar-width);
62
+ background: #17181c;
63
+ color: #e7e7ea;
64
+ position: sticky;
65
+ top: 0;
66
+ height: 100vh;
67
+ overflow-y: auto;
68
+ padding: 22px 16px;
69
+ display: flex;
70
+ flex-direction: column;
71
+ }
72
+
73
+ #book-head {
74
+ display: flex;
75
+ align-items: center;
76
+ gap: 10px;
77
+ padding: 8px;
78
+ margin-bottom: 12px;
79
+ border-radius: 10px;
80
+ cursor: pointer;
81
+ transition: background 0.12s;
82
+ }
83
+ #book-head:hover {
84
+ background: rgba(255, 255, 255, 0.05);
85
+ }
86
+ #book-wordmark {
87
+ width: 154px;
88
+ height: auto;
89
+ object-fit: contain;
90
+ }
91
+ .sr-only {
92
+ position: absolute;
93
+ width: 1px;
94
+ height: 1px;
95
+ padding: 0;
96
+ margin: -1px;
97
+ overflow: hidden;
98
+ clip: rect(0, 0, 0, 0);
99
+ white-space: nowrap;
100
+ border: 0;
101
+ }
102
+
103
+ #tree {
104
+ flex: 1;
105
+ padding-top: 8px;
106
+ }
107
+
108
+ #tree .tree-label {
109
+ padding: 6px 10px 8px;
110
+ color: #777a83;
111
+ font-size: 10px;
112
+ font-weight: 700;
113
+ letter-spacing: 0.12em;
114
+ text-transform: uppercase;
115
+ }
116
+
117
+ #tree a {
118
+ display: block;
119
+ padding: 6px 10px;
120
+ border-radius: 8px;
121
+ color: #c3c4cb;
122
+ text-decoration: none;
123
+ font-size: 14px;
124
+ transition: background 0.12s, color 0.12s;
125
+ overflow: hidden;
126
+ text-overflow: ellipsis;
127
+ white-space: nowrap;
128
+ }
129
+
130
+ #tree a:hover {
131
+ background: rgba(255, 255, 255, 0.06);
132
+ color: #ffffff;
133
+ }
134
+
135
+ #tree a.active {
136
+ background: rgba(249, 115, 22, 0.16);
137
+ color: #fdba74;
138
+ font-weight: 600;
139
+ }
140
+
141
+ #tree a .tree-mark {
142
+ color: #6b6d76;
143
+ }
144
+
145
+ #tree a:hover .tree-mark,
146
+ #tree a.active .tree-mark {
147
+ color: inherit;
148
+ opacity: 0.6;
149
+ }
150
+
151
+ #tree .depth-1 {
152
+ padding-left: 22px;
153
+ }
154
+ #tree .depth-2 {
155
+ padding-left: 34px;
156
+ }
157
+ #tree .depth-3 {
158
+ padding-left: 46px;
159
+ }
160
+
161
+
162
+ /* ---- content ---- */
163
+ #content {
164
+ flex: 1;
165
+ min-width: 0;
166
+ padding: 24px
167
+ clamp(
168
+ var(--content-gutter),
169
+ calc(100vw - 960px),
170
+ calc(var(--sidebar-width) + var(--content-gutter))
171
+ )
172
+ 120px var(--content-gutter);
173
+ background-color: var(--paper);
174
+ background-image:
175
+ linear-gradient(var(--grid-line) 1px, transparent 1px),
176
+ linear-gradient(90deg, var(--grid-line) 1px, transparent 1px);
177
+ background-size: 26px 26px;
178
+ background-position: center top;
179
+ }
180
+
181
+ #logbook-header {
182
+ width: 100%;
183
+ max-width: 1080px;
184
+ margin: 0 auto 20px;
185
+ }
186
+ #logbook-title {
187
+ font-family: var(--serif);
188
+ font-size: 34px;
189
+ line-height: 1.15;
190
+ letter-spacing: -0.02em;
191
+ margin: 0 0 10px;
192
+ overflow-wrap: anywhere;
193
+ }
194
+ #logbook-cli {
195
+ display: grid;
196
+ gap: 7px;
197
+ }
198
+
199
+ #page {
200
+ width: 100%;
201
+ min-width: 0;
202
+ max-width: 1080px;
203
+ margin: 0 auto;
204
+ }
205
+
206
+ .page-section {
207
+ scroll-margin-top: 40px;
208
+ padding: 0 0 35px;
209
+ margin: 0 0 32px;
210
+ }
211
+
212
+ .page-section:last-child {
213
+ margin-bottom: 0;
214
+ }
215
+
216
+ .page-layout {
217
+ display: block;
218
+ }
219
+
220
+ .page-body {
221
+ min-width: 0;
222
+ }
223
+
224
+ .resource-anchor {
225
+ display: block;
226
+ height: 0;
227
+ overflow: hidden;
228
+ }
229
+
230
+ /* ---- pinned notes ---- */
231
+ .pinned-notes {
232
+ margin: 30px 0 32px;
233
+ }
234
+ .pinned-notes-list .cell {
235
+ margin: 0;
236
+ }
237
+ .pinned-notes-list .cell-title {
238
+ display: flex;
239
+ align-items: center;
240
+ gap: 7px;
241
+ }
242
+ .pin-ico {
243
+ flex: 0 0 auto;
244
+ width: 14px;
245
+ height: 14px;
246
+ fill: var(--accent);
247
+ stroke: none;
248
+ }
249
+ .pinned-notes-list .cell + .cell {
250
+ margin-top: 12px;
251
+ }
252
+ .book-intro.has-pinned-notes {
253
+ border-bottom: none;
254
+ padding-bottom: 22px;
255
+ margin-bottom: 30px;
256
+ }
257
+ .book-intro.book-intro-tight {
258
+ border-bottom: none;
259
+ padding-bottom: 4px;
260
+ margin-bottom: 20px;
261
+ }
262
+
263
+ #page h1 {
264
+ font-family: var(--serif);
265
+ font-size: 34px;
266
+ line-height: 1.15;
267
+ letter-spacing: -0.02em;
268
+ margin: 0 0 8px;
269
+ overflow-wrap: anywhere;
270
+ }
271
+
272
+ #page .page-section:not(.book-intro) h1 {
273
+ font-size: 26px;
274
+ }
275
+
276
+ #page h2 {
277
+ font-family: var(--serif);
278
+ font-size: 24px;
279
+ margin: 36px 0 10px;
280
+ }
281
+
282
+ #page h3 {
283
+ font-size: 17px;
284
+ font-weight: 700;
285
+ margin: 26px 0 2px;
286
+ letter-spacing: -0.01em;
287
+ }
288
+
289
+ #page h3::before {
290
+ content: "";
291
+ display: inline-block;
292
+ width: 7px;
293
+ height: 7px;
294
+ border-radius: 2px;
295
+ background: var(--accent);
296
+ margin-right: 10px;
297
+ vertical-align: middle;
298
+ transform: translateY(-1px);
299
+ }
300
+
301
+ #page p {
302
+ margin: 10px 0;
303
+ }
304
+
305
+ #page blockquote {
306
+ margin: 14px 0;
307
+ padding: 2px 16px;
308
+ border-left: 3px solid #fdba74;
309
+ color: var(--muted);
310
+ }
311
+
312
+ #page hr {
313
+ display: none;
314
+ }
315
+
316
+ #page code {
317
+ font-family: var(--mono);
318
+ font-size: 0.86em;
319
+ background: var(--code-bg);
320
+ padding: 2px 6px;
321
+ border-radius: 6px;
322
+ }
323
+
324
+ #page pre {
325
+ max-width: 100%;
326
+ background: var(--code-bg);
327
+ border: 1px solid var(--line);
328
+ border-radius: var(--radius);
329
+ padding: 14px 16px;
330
+ overflow-x: auto;
331
+ }
332
+ #page pre code {
333
+ background: none;
334
+ padding: 0;
335
+ font-size: 11.5px;
336
+ }
337
+
338
+ /* ---- code blocks + collapsible accordion ---- */
339
+ #page pre.hl {
340
+ background: #17181c;
341
+ border: none;
342
+ color: #e7e7ea;
343
+ font-size: 13px;
344
+ line-height: 1.58;
345
+ }
346
+ #page pre.hl code {
347
+ color: inherit;
348
+ font-family: var(--mono);
349
+ }
350
+ .code-accordion {
351
+ border: 1px solid rgba(249, 115, 22, 0.2);
352
+ border-radius: 8px;
353
+ overflow: hidden;
354
+ margin: 12px 0;
355
+ background: #17181c;
356
+ }
357
+ .code-accordion summary {
358
+ list-style: none;
359
+ cursor: pointer;
360
+ display: flex;
361
+ align-items: center;
362
+ gap: 9px;
363
+ padding: 9px 12px;
364
+ font-family: var(--mono);
365
+ font-size: 11.5px;
366
+ font-weight: 700;
367
+ color: #e7e7ea;
368
+ background: #1e2027;
369
+ user-select: none;
370
+ overflow-wrap: anywhere;
371
+ }
372
+ .code-accordion summary::-webkit-details-marker {
373
+ display: none;
374
+ }
375
+ .code-accordion summary::after {
376
+ content: "▸";
377
+ margin-left: auto;
378
+ color: var(--accent);
379
+ transition: transform 0.12s;
380
+ transform: rotate(180deg);
381
+ }
382
+ .code-accordion[open] summary::after {
383
+ transform: rotate(90deg);
384
+ }
385
+ .code-accordion .code-ico {
386
+ color: var(--accent);
387
+ font-weight: 700;
388
+ }
389
+ .code-accordion pre.hl {
390
+ margin: 0;
391
+ border-radius: 0;
392
+ border: none;
393
+ border-top: 1px solid rgba(249, 115, 22, 0.16);
394
+ }
395
+ .tok-comment {
396
+ color: #7a7d87;
397
+ font-style: italic;
398
+ }
399
+ .tok-string {
400
+ color: #a5d6a7;
401
+ }
402
+ .tok-keyword {
403
+ color: #fdba74;
404
+ }
405
+ .tok-number {
406
+ color: #7fd0e0;
407
+ }
408
+
409
+ #page a {
410
+ color: var(--accent);
411
+ }
412
+
413
+ #page ul {
414
+ padding-left: 20px;
415
+ }
416
+
417
+ .ts {
418
+ font-family: var(--mono);
419
+ font-size: 12px;
420
+ color: var(--muted);
421
+ background: none;
422
+ padding: 0;
423
+ }
424
+
425
+ /* ---- notebook-style cells ---- */
426
+ .cell {
427
+ max-width: 100%;
428
+ margin: 0 0 32px;
429
+ background: none;
430
+ border: none;
431
+ border-radius: 0;
432
+ box-shadow: none;
433
+ overflow: visible;
434
+ }
435
+ .cell-head {
436
+ display: flex;
437
+ justify-content: space-between;
438
+ gap: 16px;
439
+ align-items: baseline;
440
+ padding: 0 0 5px;
441
+ background: none;
442
+ border-bottom: none;
443
+ }
444
+ .cell-head.no-title {
445
+ justify-content: flex-end;
446
+ padding: 0 0 3px;
447
+ }
448
+ .cell-title {
449
+ flex: 1;
450
+ min-width: 0;
451
+ font-size: 13px;
452
+ font-weight: 650;
453
+ color: var(--ink);
454
+ line-height: 1.35;
455
+ overflow-wrap: anywhere;
456
+ }
457
+ .cell-meta {
458
+ flex: 0 0 auto;
459
+ display: flex;
460
+ align-items: center;
461
+ gap: 10px;
462
+ font-family: var(--sans);
463
+ font-size: 13px;
464
+ color: var(--muted);
465
+ }
466
+ .cell-open {
467
+ flex: 0 0 auto;
468
+ font-family: var(--mono);
469
+ font-size: 12px;
470
+ color: var(--accent);
471
+ text-decoration: none;
472
+ }
473
+ .cell-open:hover {
474
+ color: var(--accent-strong);
475
+ }
476
+ .cell-body {
477
+ min-width: 0;
478
+ padding: 0;
479
+ }
480
+ .cell.dashboard .cell-body {
481
+ padding: 0;
482
+ }
483
+ #page .cell-body h1,
484
+ #page .cell-body h2 {
485
+ font-family: var(--sans);
486
+ font-size: 17px;
487
+ font-weight: 700;
488
+ letter-spacing: -0.01em;
489
+ line-height: 1.35;
490
+ margin: 22px 0 6px;
491
+ }
492
+ #page .cell-body > :first-child {
493
+ margin-top: 0;
494
+ }
495
+ #page .cell-body > :last-child {
496
+ margin-bottom: 0;
497
+ }
498
+ .figure-fit {
499
+ position: relative;
500
+ overflow: hidden;
501
+ min-height: 160px;
502
+ border: 1px solid var(--line);
503
+ border-radius: 8px;
504
+ background: #fff;
505
+ }
506
+ .figure-fit[hidden] {
507
+ display: none;
508
+ }
509
+ .figure-fit:fullscreen,
510
+ .figure-fit:-webkit-full-screen {
511
+ width: 100%;
512
+ height: 100%;
513
+ border: none;
514
+ border-radius: 0;
515
+ }
516
+ .figure-frame {
517
+ display: block;
518
+ width: 100%;
519
+ min-height: 160px;
520
+ border: none;
521
+ background: #fff;
522
+ }
523
+ .figure-frame[hidden],
524
+ .figure-raw[hidden] {
525
+ display: none;
526
+ }
527
+ .fig-switch {
528
+ position: relative;
529
+ display: inline-flex;
530
+ flex: 0 0 auto;
531
+ border: 1px solid var(--line);
532
+ border-radius: 999px;
533
+ background: var(--code-bg);
534
+ padding: 2px;
535
+ }
536
+ .fig-switch button {
537
+ position: relative;
538
+ z-index: 1;
539
+ flex: 1;
540
+ min-width: 62px;
541
+ border: none;
542
+ background: none;
543
+ font-family: var(--sans);
544
+ font-size: 12px;
545
+ font-weight: 600;
546
+ color: var(--muted);
547
+ padding: 3px 12px;
548
+ border-radius: 999px;
549
+ cursor: pointer;
550
+ transition: color 0.15s;
551
+ }
552
+ .fig-switch button.active {
553
+ color: var(--accent-strong);
554
+ }
555
+ .fig-switch-thumb {
556
+ position: absolute;
557
+ top: 2px;
558
+ bottom: 2px;
559
+ left: 2px;
560
+ width: calc(50% - 2px);
561
+ border-radius: 999px;
562
+ background: var(--panel);
563
+ border: 1px solid rgba(249, 115, 22, 0.35);
564
+ box-shadow: 0 1px 4px rgba(31, 41, 55, 0.08);
565
+ transition: transform 0.18s ease;
566
+ }
567
+ .fig-switch.raw .fig-switch-thumb {
568
+ transform: translateX(100%);
569
+ }
570
+ #page .figure-raw pre {
571
+ margin: 0;
572
+ max-height: 420px;
573
+ overflow: auto;
574
+ font-family: var(--mono);
575
+ font-size: 13px;
576
+ line-height: 1.55;
577
+ background: var(--code-bg);
578
+ border: 1px solid var(--line);
579
+ border-radius: 8px;
580
+ padding: 12px 14px;
581
+ }
582
+ /* ---- figure fullscreen ---- */
583
+ .cell-fullscreen {
584
+ position: relative;
585
+ display: inline-flex;
586
+ flex: 0 0 auto;
587
+ }
588
+ .cell-fullscreen-btn {
589
+ display: inline-flex;
590
+ align-items: center;
591
+ justify-content: center;
592
+ width: 26px;
593
+ height: 26px;
594
+ padding: 0;
595
+ border: 1px solid var(--line);
596
+ border-radius: 999px;
597
+ background: var(--code-bg);
598
+ color: var(--muted);
599
+ cursor: pointer;
600
+ transition: color 0.15s, border-color 0.15s, background 0.15s;
601
+ }
602
+ .cell-fullscreen-btn:hover {
603
+ color: var(--accent-strong);
604
+ border-color: rgba(249, 115, 22, 0.35);
605
+ background: var(--accent-soft);
606
+ }
607
+ .cell-fullscreen-btn svg {
608
+ width: 14px;
609
+ height: 14px;
610
+ }
611
+ /* ---- copyable snippets ---- */
612
+ .snippet {
613
+ position: relative;
614
+ }
615
+ .copy-snippet {
616
+ position: absolute;
617
+ top: 7px;
618
+ right: 8px;
619
+ width: 24px;
620
+ height: 24px;
621
+ border: none;
622
+ border-radius: 6px;
623
+ background: rgba(255, 255, 255, 0.08);
624
+ color: #9a9da8;
625
+ font-size: 12px;
626
+ line-height: 1;
627
+ cursor: pointer;
628
+ opacity: 0;
629
+ transition: opacity 0.12s, color 0.12s, background 0.12s;
630
+ }
631
+ .snippet:hover .copy-snippet,
632
+ .jp-out:hover .copy-snippet,
633
+ .figure-raw:hover .copy-snippet,
634
+ .code-accordion summary:hover .copy-snippet {
635
+ opacity: 1;
636
+ }
637
+ .copy-snippet:hover {
638
+ color: #ffffff;
639
+ background: rgba(255, 255, 255, 0.16);
640
+ }
641
+ .copy-snippet.copied {
642
+ color: #52d08a;
643
+ opacity: 1;
644
+ }
645
+ .code-accordion .code-name {
646
+ user-select: text;
647
+ cursor: text;
648
+ }
649
+ .jp-out,
650
+ .figure-raw {
651
+ position: relative;
652
+ }
653
+ .jp-out .copy-snippet,
654
+ .figure-raw .copy-snippet {
655
+ background: var(--code-bg);
656
+ color: var(--muted);
657
+ border: 1px solid var(--line);
658
+ }
659
+ .jp-out .copy-snippet:hover,
660
+ .figure-raw .copy-snippet:hover {
661
+ color: var(--accent-strong);
662
+ background: var(--panel);
663
+ }
664
+
665
+ /* ---- jupyter-style code cells ---- */
666
+ .jp {
667
+ border: 1px solid var(--line);
668
+ border-radius: 10px;
669
+ overflow: hidden;
670
+ margin: 0;
671
+ background: var(--panel);
672
+ }
673
+ .jp-cmd {
674
+ display: flex;
675
+ align-items: baseline;
676
+ gap: 9px;
677
+ position: relative;
678
+ padding: 10px 16px 10px 0;
679
+ font-family: var(--mono);
680
+ font-size: 12px;
681
+ color: #8b8e98;
682
+ }
683
+ .jp-cmd-prompt {
684
+ color: var(--accent);
685
+ font-weight: 700;
686
+ }
687
+ #page .jp-cmd code {
688
+ min-width: 0;
689
+ color: #b6b9c2;
690
+ font-family: var(--mono);
691
+ font-size: 12px;
692
+ background: none;
693
+ padding: 0;
694
+ border-radius: 0;
695
+ overflow-wrap: anywhere;
696
+ }
697
+ .jp-cmd:hover .copy-snippet {
698
+ opacity: 1;
699
+ }
700
+ .jp-in-body .jp-cmd + .code-accordion,
701
+ .jp-in-body .jp-cmd + .snippet {
702
+ border-top: 1px solid rgba(255, 255, 255, 0.09);
703
+ }
704
+ .jp-gutter {
705
+ flex: 0 0 46px;
706
+ padding: 13px 0 0 13px;
707
+ font-family: var(--mono);
708
+ font-size: 10.5px;
709
+ letter-spacing: 0.07em;
710
+ text-transform: uppercase;
711
+ font-weight: 600;
712
+ user-select: none;
713
+ }
714
+ .jp-in {
715
+ display: flex;
716
+ background: #17181c;
717
+ }
718
+ .jp-in .jp-gutter {
719
+ color: #6f727d;
720
+ }
721
+ .jp-in-body {
722
+ flex: 1;
723
+ min-width: 0;
724
+ }
725
+ #page .jp-in-body pre.hl {
726
+ margin: 0;
727
+ border: none;
728
+ border-radius: 0;
729
+ background: none;
730
+ padding: 12px 16px 12px 0;
731
+ overflow-y: auto;
732
+ max-height: 26em;
733
+ }
734
+ .jp-in-body .code-accordion {
735
+ margin: 0;
736
+ border: none;
737
+ border-top: 1px solid rgba(255, 255, 255, 0.09);
738
+ border-radius: 0;
739
+ background: none;
740
+ }
741
+ .jp-in-body .code-accordion summary {
742
+ background: none;
743
+ padding: 9px 16px 9px 0;
744
+ }
745
+ .jp-in-body .code-accordion pre.hl {
746
+ border-top: 1px solid rgba(255, 255, 255, 0.09);
747
+ }
748
+ .jp-meta {
749
+ padding: 5px 14px;
750
+ font-family: var(--mono);
751
+ font-size: 11.5px;
752
+ color: var(--muted);
753
+ background: #fbfbfc;
754
+ border-top: 1px solid var(--line);
755
+ }
756
+ .jp-out {
757
+ display: flex;
758
+ border-top: 1px solid var(--line);
759
+ background: var(--panel);
760
+ }
761
+ .jp-out .jp-gutter {
762
+ color: var(--accent-strong);
763
+ }
764
+ .jp-out-body {
765
+ flex: 1;
766
+ min-width: 0;
767
+ }
768
+ #page .jp-out-pre {
769
+ min-width: 0;
770
+ margin: 0;
771
+ border: none;
772
+ border-radius: 0;
773
+ background: none;
774
+ color: var(--ink);
775
+ font-family: var(--mono);
776
+ font-size: 13px;
777
+ line-height: 1.55;
778
+ padding: 12px 16px 12px 0;
779
+ white-space: pre;
780
+ overflow-x: auto;
781
+ overflow-y: auto;
782
+ max-height: 26em;
783
+ }
784
+ .jp-artifacts {
785
+ display: flex;
786
+ flex-direction: column;
787
+ }
788
+ .jp-out-body .jp-out-pre + .jp-artifacts {
789
+ border-top: 1px solid var(--line);
790
+ }
791
+ .out-artifact {
792
+ display: flex;
793
+ align-items: baseline;
794
+ gap: 8px;
795
+ padding: 9px 16px 9px 0;
796
+ text-decoration: none;
797
+ color: inherit;
798
+ }
799
+ .out-artifact + .out-artifact {
800
+ border-top: 1px solid var(--line);
801
+ }
802
+ a.out-artifact:hover .out-artifact-name {
803
+ color: var(--accent-strong);
804
+ }
805
+ .out-artifact-ico {
806
+ flex: 0 0 auto;
807
+ font-size: 13px;
808
+ }
809
+ .out-artifact-name {
810
+ font-family: var(--mono);
811
+ font-size: 12.5px;
812
+ font-weight: 600;
813
+ color: var(--ink);
814
+ overflow: hidden;
815
+ text-overflow: ellipsis;
816
+ white-space: nowrap;
817
+ }
818
+ .out-artifact-meta {
819
+ flex: 0 0 auto;
820
+ margin-left: auto;
821
+ padding-left: 12px;
822
+ font-size: 12px;
823
+ color: var(--muted);
824
+ white-space: nowrap;
825
+ }
826
+ .out-artifact-state.open {
827
+ color: var(--accent);
828
+ font-weight: 600;
829
+ }
830
+ .trackio-embed {
831
+ border: 1px solid var(--line);
832
+ border-radius: var(--radius);
833
+ overflow: hidden;
834
+ background: var(--panel);
835
+ }
836
+ .trackio-cell-meta {
837
+ display: flex;
838
+ gap: 6px;
839
+ flex-wrap: wrap;
840
+ justify-content: flex-end;
841
+ }
842
+
843
+ /* ---- unfurl cards ---- */
844
+ .unfurl {
845
+ display: block;
846
+ border: 1px solid var(--line);
847
+ border-radius: var(--radius);
848
+ background: var(--panel);
849
+ margin: 12px 0;
850
+ overflow: hidden;
851
+ text-decoration: none;
852
+ color: inherit;
853
+ transition: border-color 0.14s, box-shadow 0.14s;
854
+ }
855
+ .unfurl:hover {
856
+ border-color: #cfcbe6;
857
+ box-shadow: 0 4px 18px rgba(30, 20, 80, 0.06);
858
+ }
859
+
860
+ .unfurl-body {
861
+ padding: 13px 16px;
862
+ display: flex;
863
+ gap: 12px;
864
+ align-items: flex-start;
865
+ }
866
+
867
+ .unfurl-ico {
868
+ font-size: 20px;
869
+ line-height: 1.3;
870
+ flex: 0 0 auto;
871
+ }
872
+
873
+ .unfurl-main {
874
+ min-width: 0;
875
+ flex: 1;
876
+ }
877
+
878
+ .unfurl-kind {
879
+ font-family: var(--mono);
880
+ font-size: 10.5px;
881
+ text-transform: uppercase;
882
+ letter-spacing: 0.08em;
883
+ color: var(--accent);
884
+ font-weight: 600;
885
+ }
886
+
887
+ .unfurl-title {
888
+ font-weight: 650;
889
+ font-size: 15px;
890
+ margin: 1px 0 2px;
891
+ white-space: nowrap;
892
+ overflow: hidden;
893
+ text-overflow: ellipsis;
894
+ }
895
+
896
+ .unfurl-desc {
897
+ color: var(--muted);
898
+ font-size: 13.5px;
899
+ line-height: 1.45;
900
+ }
901
+
902
+ .unfurl-meta {
903
+ margin-top: 6px;
904
+ display: flex;
905
+ flex-wrap: wrap;
906
+ gap: 6px;
907
+ }
908
+
909
+ .chip {
910
+ font-size: 11.5px;
911
+ background: var(--code-bg);
912
+ border-radius: 999px;
913
+ padding: 2px 9px;
914
+ color: var(--muted);
915
+ font-family: var(--mono);
916
+ }
917
+
918
+ .unfurl-raw {
919
+ font-family: var(--mono);
920
+ font-size: 11px;
921
+ color: var(--muted);
922
+ border-top: 1px solid var(--line);
923
+ padding: 7px 16px;
924
+ white-space: nowrap;
925
+ overflow: hidden;
926
+ text-overflow: ellipsis;
927
+ }
928
+
929
+ .unfurl.embed {
930
+ padding: 0;
931
+ overflow: hidden;
932
+ }
933
+ .embed-head {
934
+ display: flex;
935
+ align-items: center;
936
+ gap: 10px;
937
+ padding: 10px 14px;
938
+ border-bottom: 1px solid var(--line);
939
+ }
940
+ .embed-head .unfurl-kind {
941
+ flex: 0 0 auto;
942
+ }
943
+ .embed-title {
944
+ flex: 1;
945
+ min-width: 0;
946
+ font-weight: 650;
947
+ font-size: 14px;
948
+ color: var(--ink);
949
+ text-decoration: none;
950
+ white-space: nowrap;
951
+ overflow: hidden;
952
+ text-overflow: ellipsis;
953
+ }
954
+ .embed-title:hover {
955
+ color: var(--accent);
956
+ }
957
+ .embed-open {
958
+ flex: 0 0 auto;
959
+ font-family: var(--mono);
960
+ font-size: 12px;
961
+ color: var(--accent);
962
+ text-decoration: none;
963
+ }
964
+ .embed-frame {
965
+ display: block;
966
+ width: 100%;
967
+ height: 560px;
968
+ border: 0;
969
+ background: var(--code-bg);
970
+ }
971
+
972
+ .dashboard-shell {
973
+ display: block;
974
+ }
975
+ .dashboard-shell .dashboard-frame {
976
+ display: block;
977
+ width: 100%;
978
+ height: 900px;
979
+ border: 0;
980
+ background: var(--code-bg);
981
+ }
982
+
983
+ .unfurl.image {
984
+ padding: 0;
985
+ }
986
+ .unfurl.image img {
987
+ display: block;
988
+ width: 100%;
989
+ height: auto;
990
+ max-height: 460px;
991
+ object-fit: contain;
992
+ background: var(--code-bg);
993
+ }
994
+
995
+ .artifact-chip {
996
+ border: 1px solid var(--line);
997
+ background: var(--panel);
998
+ border-radius: var(--radius);
999
+ padding: 10px 14px;
1000
+ margin: 8px 0;
1001
+ font-size: 14px;
1002
+ }
1003
+ .cell.dashboard .artifact-chip {
1004
+ margin: 14px 18px 18px;
1005
+ }
1006
+ .artifact-chip code {
1007
+ color: var(--accent);
1008
+ }
1009
+
1010
+ /* ---- task board ---- */
1011
+ .board-wrap {
1012
+ overflow-x: auto;
1013
+ border: 1px solid var(--line);
1014
+ border-radius: var(--radius);
1015
+ margin: 12px 0 20px;
1016
+ background: var(--panel);
1017
+ }
1018
+ table.board {
1019
+ border-collapse: collapse;
1020
+ width: 100%;
1021
+ font-size: 14px;
1022
+ }
1023
+ table.board th,
1024
+ table.board td {
1025
+ text-align: left;
1026
+ padding: 9px 14px;
1027
+ border-bottom: 1px solid var(--line);
1028
+ vertical-align: top;
1029
+ }
1030
+ table.board thead th {
1031
+ background: var(--accent-soft);
1032
+ font-size: 12px;
1033
+ text-transform: uppercase;
1034
+ letter-spacing: 0.05em;
1035
+ color: #9a4a12;
1036
+ font-weight: 600;
1037
+ border-bottom: 1px solid var(--line);
1038
+ }
1039
+ table.board tbody tr:last-child td {
1040
+ border-bottom: none;
1041
+ }
1042
+ table.board .col-check {
1043
+ text-align: center;
1044
+ width: 92px;
1045
+ white-space: nowrap;
1046
+ }
1047
+ table.board tr.section-row td {
1048
+ background: var(--accent-soft);
1049
+ text-align: center;
1050
+ font-weight: 700;
1051
+ font-size: 13px;
1052
+ color: var(--accent-strong);
1053
+ padding: 7px 14px;
1054
+ letter-spacing: 0.02em;
1055
+ }
1056
+ .box {
1057
+ display: inline-flex;
1058
+ align-items: center;
1059
+ justify-content: center;
1060
+ width: 18px;
1061
+ height: 18px;
1062
+ border: 1.5px solid #cfcbe0;
1063
+ border-radius: 5px;
1064
+ font-size: 12px;
1065
+ color: #fff;
1066
+ line-height: 1;
1067
+ }
1068
+ .box.on {
1069
+ background: var(--accent);
1070
+ border-color: var(--accent);
1071
+ }
1072
+ .who-chip {
1073
+ display: inline-block;
1074
+ padding: 3px 12px;
1075
+ border-radius: 999px;
1076
+ font-size: 12.5px;
1077
+ font-weight: 600;
1078
+ white-space: nowrap;
1079
+ }
1080
+ .who-chip.muted {
1081
+ background: var(--code-bg);
1082
+ color: var(--muted);
1083
+ font-weight: 500;
1084
+ }
1085
+
1086
+ /* ---- status badges + clickable rows ---- */
1087
+ table.board .col-status {
1088
+ width: 130px;
1089
+ white-space: nowrap;
1090
+ }
1091
+ .badge {
1092
+ display: inline-block;
1093
+ padding: 3px 11px;
1094
+ border-radius: 999px;
1095
+ font-size: 12px;
1096
+ font-weight: 600;
1097
+ letter-spacing: 0.01em;
1098
+ }
1099
+ .badge.gray {
1100
+ background: var(--code-bg);
1101
+ color: var(--muted);
1102
+ }
1103
+ .badge.amber {
1104
+ background: var(--accent-soft);
1105
+ color: #b45309;
1106
+ }
1107
+ .badge.green {
1108
+ background: #e6f7ee;
1109
+ color: #1a8a55;
1110
+ }
1111
+ .badge.red {
1112
+ background: #fde8ec;
1113
+ color: #c62a4b;
1114
+ }
1115
+ table.board tr.linked-row {
1116
+ cursor: pointer;
1117
+ }
1118
+ table.board tr.linked-row:hover td {
1119
+ background: var(--accent-soft);
1120
+ }
1121
+ table.board tr.linked-row a {
1122
+ color: var(--ink);
1123
+ font-weight: 600;
1124
+ text-decoration: none;
1125
+ }
1126
+ table.board tr.linked-row:hover a {
1127
+ color: var(--accent-strong);
1128
+ }
1129
+
1130
+ /* ---- agent read hint ---- */
1131
+ .agent-hint {
1132
+ display: flex;
1133
+ align-items: center;
1134
+ flex-wrap: wrap;
1135
+ gap: 8px;
1136
+ margin: 0;
1137
+ font-size: 12.5px;
1138
+ color: var(--muted);
1139
+ }
1140
+ .agent-hint code {
1141
+ flex: 1 1 18rem;
1142
+ min-width: 0;
1143
+ background: var(--code-bg);
1144
+ padding: 2px 9px;
1145
+ border-radius: 6px;
1146
+ font-family: var(--mono);
1147
+ font-size: 12px;
1148
+ font-weight: 500;
1149
+ color: var(--ink);
1150
+ overflow: hidden;
1151
+ text-overflow: ellipsis;
1152
+ white-space: nowrap;
1153
+ }
1154
+ .agent-hint .copy {
1155
+ flex: 0 0 auto;
1156
+ background: none;
1157
+ color: var(--muted);
1158
+ border: 1px solid var(--line);
1159
+ border-radius: 6px;
1160
+ width: 22px;
1161
+ height: 22px;
1162
+ font-size: 11px;
1163
+ line-height: 1;
1164
+ cursor: pointer;
1165
+ transition: color 0.12s, border-color 0.12s;
1166
+ }
1167
+ .agent-hint .copy:hover {
1168
+ color: var(--accent-strong);
1169
+ border-color: var(--accent);
1170
+ }
1171
+ .agent-hint .copy.copied {
1172
+ color: #1a8a55;
1173
+ border-color: #1a8a55;
1174
+ }
1175
+ .agent-hint-note {
1176
+ margin-left: auto;
1177
+ font-size: 12px;
1178
+ color: var(--muted);
1179
+ }
1180
+ .hub-destination {
1181
+ display: flex;
1182
+ align-items: center;
1183
+ flex-wrap: wrap;
1184
+ gap: 8px;
1185
+ color: var(--muted);
1186
+ font-size: 12.5px;
1187
+ }
1188
+ .hub-destination a {
1189
+ display: inline-flex;
1190
+ align-items: center;
1191
+ gap: 6px;
1192
+ max-width: 100%;
1193
+ padding: 3px 9px;
1194
+ border: 1px solid var(--accent-line);
1195
+ border-radius: 999px;
1196
+ background: var(--accent-soft);
1197
+ color: var(--accent-strong);
1198
+ font-family: var(--mono);
1199
+ font-size: 12px;
1200
+ font-weight: 650;
1201
+ line-height: 1.5;
1202
+ text-decoration: none;
1203
+ overflow-wrap: anywhere;
1204
+ transition: border-color 0.12s, background 0.12s, color 0.12s;
1205
+ }
1206
+ .hub-destination a:hover {
1207
+ border-color: var(--accent);
1208
+ background: #ffedd5;
1209
+ color: #c2410c;
1210
+ }
1211
+ .hub-destination svg {
1212
+ width: 13px;
1213
+ height: 13px;
1214
+ flex: 0 0 auto;
1215
+ fill: none;
1216
+ stroke: currentColor;
1217
+ stroke-width: 1.8;
1218
+ stroke-linecap: round;
1219
+ stroke-linejoin: round;
1220
+ }
1221
+
1222
+ .index-paper-link {
1223
+ margin: 14px 0 30px;
1224
+ font-size: 19px;
1225
+ line-height: 1.35;
1226
+ font-weight: 700;
1227
+ }
1228
+ .index-paper-link a {
1229
+ text-underline-offset: 4px;
1230
+ text-decoration-thickness: 2px;
1231
+ }
1232
+ .art-ico {
1233
+ width: 1em;
1234
+ height: 1em;
1235
+ object-fit: contain;
1236
+ vertical-align: -0.15em;
1237
+ }
1238
+ .art-file-ico {
1239
+ width: 15px;
1240
+ height: 15px;
1241
+ flex: 0 0 auto;
1242
+ fill: none;
1243
+ stroke: currentColor;
1244
+ stroke-width: 1.7;
1245
+ stroke-linecap: round;
1246
+ stroke-linejoin: round;
1247
+ vertical-align: -0.2em;
1248
+ }
1249
+ .out-artifact-ico .art-file-ico {
1250
+ color: var(--muted);
1251
+ }
1252
+
1253
+ /* ---- scroll-to-resource highlight ---- */
1254
+ .res-flash {
1255
+ animation: res-flash 1.5s ease;
1256
+ border-radius: 8px;
1257
+ }
1258
+ @keyframes res-flash {
1259
+ 0%,
1260
+ 25% {
1261
+ box-shadow: 0 0 0 3px var(--accent);
1262
+ }
1263
+ 100% {
1264
+ box-shadow: 0 0 0 3px rgba(249, 115, 22, 0);
1265
+ }
1266
+ }
1267
+
1268
+ /* ---- inline resource chips ---- */
1269
+ #page .res-chip {
1270
+ display: inline-flex;
1271
+ align-items: center;
1272
+ gap: 5px;
1273
+ max-width: 100%;
1274
+ padding: 0 9px 0 6px;
1275
+ margin: 0 1px;
1276
+ border: 1px solid var(--line);
1277
+ border-radius: 999px;
1278
+ background: var(--panel);
1279
+ font-family: var(--mono);
1280
+ font-size: 0.78em;
1281
+ font-weight: 600;
1282
+ color: var(--ink);
1283
+ text-decoration: none;
1284
+ white-space: nowrap;
1285
+ overflow: hidden;
1286
+ text-overflow: ellipsis;
1287
+ vertical-align: middle;
1288
+ line-height: 1.65;
1289
+ transform: translateY(-0.08em);
1290
+ transition: border-color 0.12s, background 0.12s, color 0.12s;
1291
+ }
1292
+ .res-chip-ico {
1293
+ font-size: 1.05em;
1294
+ line-height: 1;
1295
+ }
1296
+ #page .res-chip:hover {
1297
+ border-color: var(--accent);
1298
+ background: var(--accent-soft);
1299
+ color: var(--accent-strong);
1300
+ }
1301
+
1302
+ /* ---- connect footer + modal ---- */
1303
+ #sidebar-foot {
1304
+ margin-top: auto;
1305
+ padding-top: 14px;
1306
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
1307
+ }
1308
+
1309
+ #connect-btn {
1310
+ width: 100%;
1311
+ display: flex;
1312
+ align-items: center;
1313
+ gap: 8px;
1314
+ background: rgba(255, 255, 255, 0.05);
1315
+ color: #c3c4cb;
1316
+ border: 1px solid rgba(255, 255, 255, 0.12);
1317
+ border-radius: 9px;
1318
+ padding: 9px 12px;
1319
+ font-size: 13.5px;
1320
+ font-family: var(--sans);
1321
+ cursor: pointer;
1322
+ transition: background 0.12s, color 0.12s, border-color 0.12s;
1323
+ }
1324
+ #connect-btn:hover {
1325
+ background: rgba(249, 115, 22, 0.14);
1326
+ border-color: rgba(249, 115, 22, 0.4);
1327
+ color: #fdba74;
1328
+ }
1329
+ #connect-btn .ico {
1330
+ font-size: 15px;
1331
+ }
1332
+
1333
+ #modal[hidden] {
1334
+ display: none;
1335
+ }
1336
+ #modal {
1337
+ position: fixed;
1338
+ inset: 0;
1339
+ z-index: 100;
1340
+ display: flex;
1341
+ align-items: center;
1342
+ justify-content: center;
1343
+ padding: 24px;
1344
+ }
1345
+ .modal-backdrop {
1346
+ position: absolute;
1347
+ inset: 0;
1348
+ background: rgba(20, 18, 30, 0.5);
1349
+ backdrop-filter: blur(2px);
1350
+ }
1351
+ .modal-card {
1352
+ position: relative;
1353
+ background: var(--panel);
1354
+ border-radius: 16px;
1355
+ width: 100%;
1356
+ max-width: 620px;
1357
+ max-height: 85vh;
1358
+ overflow-y: auto;
1359
+ box-shadow: 0 24px 70px rgba(20, 15, 50, 0.28);
1360
+ }
1361
+ .modal-head {
1362
+ display: flex;
1363
+ align-items: center;
1364
+ justify-content: space-between;
1365
+ gap: 12px;
1366
+ padding: 18px 22px;
1367
+ border-bottom: 1px solid var(--line);
1368
+ position: sticky;
1369
+ top: 0;
1370
+ background: var(--panel);
1371
+ }
1372
+ .modal-title {
1373
+ display: flex;
1374
+ align-items: center;
1375
+ gap: 10px;
1376
+ font-family: var(--serif);
1377
+ font-size: 21px;
1378
+ letter-spacing: -0.01em;
1379
+ }
1380
+ .modal-logo {
1381
+ width: 26px;
1382
+ height: 26px;
1383
+ object-fit: contain;
1384
+ }
1385
+ .modal-actions {
1386
+ display: flex;
1387
+ align-items: center;
1388
+ gap: 8px;
1389
+ }
1390
+ .btn {
1391
+ font-family: var(--sans);
1392
+ font-size: 13.5px;
1393
+ font-weight: 600;
1394
+ border: 1px solid var(--line);
1395
+ background: var(--panel);
1396
+ color: var(--ink);
1397
+ border-radius: 9px;
1398
+ padding: 8px 13px;
1399
+ cursor: pointer;
1400
+ transition: background 0.12s, border-color 0.12s, color 0.12s;
1401
+ }
1402
+ .btn:hover {
1403
+ border-color: var(--accent);
1404
+ color: var(--accent-strong);
1405
+ }
1406
+ .btn.copied {
1407
+ border-color: #1a8a55;
1408
+ color: #1a8a55;
1409
+ }
1410
+ .btn.icon {
1411
+ font-size: 18px;
1412
+ line-height: 1;
1413
+ padding: 6px 11px;
1414
+ font-weight: 400;
1415
+ }
1416
+ .modal-body {
1417
+ padding: 20px 22px 26px;
1418
+ }
1419
+ .modal-intro {
1420
+ margin: 0 0 20px;
1421
+ color: var(--muted);
1422
+ line-height: 1.55;
1423
+ }
1424
+ #connect-steps {
1425
+ list-style: none;
1426
+ margin: 0;
1427
+ padding: 0;
1428
+ }
1429
+ #connect-steps li {
1430
+ margin-bottom: 18px;
1431
+ }
1432
+ .step-title {
1433
+ font-weight: 600;
1434
+ font-size: 14.5px;
1435
+ margin-bottom: 8px;
1436
+ }
1437
+ .codeblock {
1438
+ display: flex;
1439
+ align-items: center;
1440
+ gap: 8px;
1441
+ background: #17181c;
1442
+ border-radius: 10px;
1443
+ padding: 11px 12px 11px 15px;
1444
+ }
1445
+ .codeblock code {
1446
+ flex: 1;
1447
+ min-width: 0;
1448
+ overflow-x: auto;
1449
+ white-space: nowrap;
1450
+ font-family: var(--mono);
1451
+ font-size: 13px;
1452
+ color: #f0efff;
1453
+ background: none;
1454
+ padding: 0;
1455
+ }
1456
+ .codeblock .copy {
1457
+ flex: 0 0 auto;
1458
+ background: rgba(255, 255, 255, 0.08);
1459
+ color: #c3c4cb;
1460
+ border: 1px solid rgba(255, 255, 255, 0.14);
1461
+ border-radius: 7px;
1462
+ width: 30px;
1463
+ height: 30px;
1464
+ font-size: 14px;
1465
+ cursor: pointer;
1466
+ transition: background 0.12s, color 0.12s;
1467
+ }
1468
+ .codeblock .copy:hover {
1469
+ background: rgba(249, 115, 22, 0.2);
1470
+ color: #fdba74;
1471
+ }
1472
+ .codeblock .copy.copied {
1473
+ color: #52d08a;
1474
+ }
1475
+
1476
+ /* ---- top-level logbook views ---- */
1477
+ #view-tabs {
1478
+ position: sticky;
1479
+ top: 0;
1480
+ z-index: 30;
1481
+ width: 100%;
1482
+ max-width: 1080px;
1483
+ margin: 0 auto 24px;
1484
+ padding-top: 10px;
1485
+ display: flex;
1486
+ align-items: center;
1487
+ justify-content: flex-start;
1488
+ gap: 26px;
1489
+ border-bottom: 1px solid var(--line);
1490
+ background: var(--paper);
1491
+ }
1492
+ #view-tabs a {
1493
+ display: inline-flex;
1494
+ align-items: center;
1495
+ gap: 8px;
1496
+ min-height: 44px;
1497
+ margin-bottom: -1px;
1498
+ color: var(--muted);
1499
+ border-bottom: 2px solid transparent;
1500
+ text-decoration: none;
1501
+ font-size: 13.5px;
1502
+ font-weight: 600;
1503
+ transition: color 0.12s, border-color 0.12s;
1504
+ }
1505
+ #view-tabs a:hover {
1506
+ color: var(--ink);
1507
+ }
1508
+ #view-tabs a.active {
1509
+ color: var(--accent-strong);
1510
+ border-bottom-color: var(--accent);
1511
+ }
1512
+ #view-tabs svg {
1513
+ width: 18px;
1514
+ height: 18px;
1515
+ flex: 0 0 auto;
1516
+ fill: none;
1517
+ stroke: currentColor;
1518
+ stroke-width: 2;
1519
+ stroke-linecap: round;
1520
+ stroke-linejoin: round;
1521
+ }
1522
+ .workspace-file svg,
1523
+ .workspace-folder summary svg,
1524
+ .workspace-download svg {
1525
+ width: 17px;
1526
+ height: 17px;
1527
+ flex: 0 0 auto;
1528
+ fill: none;
1529
+ stroke: currentColor;
1530
+ stroke-width: 1.7;
1531
+ stroke-linecap: round;
1532
+ stroke-linejoin: round;
1533
+ }
1534
+
1535
+ #page.trace-page,
1536
+ #page.workspace-page {
1537
+ max-width: 1080px;
1538
+ }
1539
+ .view-loading {
1540
+ padding: 72px 0;
1541
+ color: var(--muted);
1542
+ text-align: center;
1543
+ }
1544
+ .view-empty {
1545
+ margin: 48px 0;
1546
+ padding: 44px 28px;
1547
+ border: 1px dashed #d8dbe1;
1548
+ border-radius: var(--radius);
1549
+ background: rgba(255, 255, 255, 0.72);
1550
+ text-align: center;
1551
+ }
1552
+ .view-empty h2 {
1553
+ margin: 0 0 7px;
1554
+ font-size: 18px;
1555
+ }
1556
+ .view-empty p {
1557
+ max-width: 560px;
1558
+ margin: 0 auto;
1559
+ color: var(--muted);
1560
+ }
1561
+ .view-empty code {
1562
+ display: inline-block;
1563
+ margin-top: 18px;
1564
+ padding: 7px 10px;
1565
+ border-radius: 7px;
1566
+ background: var(--code-bg);
1567
+ font-family: var(--mono);
1568
+ font-size: 12px;
1569
+ }
1570
+ #page .repo-ref-link {
1571
+ display: inline-block;
1572
+ margin-top: 18px;
1573
+ padding: 8px 14px;
1574
+ border-radius: 8px;
1575
+ background: var(--accent-strong, #2158d0);
1576
+ color: #fff;
1577
+ font-weight: 600;
1578
+ text-decoration: none;
1579
+ }
1580
+ #page .repo-ref-link:hover,
1581
+ #page .repo-ref-link:focus-visible {
1582
+ color: #fff;
1583
+ filter: brightness(0.95);
1584
+ }
1585
+ .view-eyebrow {
1586
+ margin-bottom: 4px;
1587
+ color: var(--accent-strong);
1588
+ font-family: var(--mono);
1589
+ font-size: 11px;
1590
+ font-weight: 700;
1591
+ letter-spacing: 0.12em;
1592
+ text-transform: uppercase;
1593
+ }
1594
+
1595
+ /* ---- trace ---- */
1596
+ .trace-session {
1597
+ scroll-margin-top: 24px;
1598
+ }
1599
+ .trace-session + .trace-session {
1600
+ margin-top: 44px;
1601
+ padding-top: 40px;
1602
+ border-top: 1px solid var(--line);
1603
+ }
1604
+ .trace-session-title {
1605
+ margin: 0 0 14px;
1606
+ color: var(--ink);
1607
+ font-family: var(--serif);
1608
+ font-size: 22px;
1609
+ line-height: 1.2;
1610
+ letter-spacing: -0.02em;
1611
+ overflow-wrap: anywhere;
1612
+ }
1613
+ .workspace-header h1 {
1614
+ margin: 0;
1615
+ color: var(--ink);
1616
+ font-size: 30px;
1617
+ line-height: 1.2;
1618
+ letter-spacing: -0.025em;
1619
+ }
1620
+ .trace-meta {
1621
+ display: flex;
1622
+ flex-wrap: wrap;
1623
+ gap: 9px 20px;
1624
+ margin-bottom: 34px;
1625
+ padding: 14px 16px;
1626
+ border: 1px solid var(--line);
1627
+ border-radius: 10px;
1628
+ background: rgba(255, 255, 255, 0.78);
1629
+ color: var(--muted);
1630
+ font-family: var(--mono);
1631
+ font-size: 11px;
1632
+ }
1633
+ .trace-meta strong {
1634
+ color: var(--ink);
1635
+ font-weight: 650;
1636
+ }
1637
+ .trace-source-missing {
1638
+ color: #b45309;
1639
+ }
1640
+ .trace-timeline {
1641
+ position: relative;
1642
+ }
1643
+ .trace-timeline::before {
1644
+ content: "";
1645
+ position: absolute;
1646
+ top: 0;
1647
+ bottom: 0;
1648
+ left: 82px;
1649
+ width: 1px;
1650
+ background: #dedfe3;
1651
+ }
1652
+ .trace-load-controls {
1653
+ display: flex;
1654
+ align-items: center;
1655
+ justify-content: space-between;
1656
+ gap: 16px;
1657
+ margin: 22px 0 0 100px;
1658
+ padding-top: 16px;
1659
+ border-top: 1px solid var(--line);
1660
+ }
1661
+ .trace-load-progress {
1662
+ color: var(--muted);
1663
+ font-family: var(--mono);
1664
+ font-size: 11px;
1665
+ }
1666
+ .trace-load-more {
1667
+ padding: 7px 12px;
1668
+ border: 1px solid var(--line-strong);
1669
+ border-radius: 7px;
1670
+ background: var(--paper);
1671
+ color: var(--ink);
1672
+ cursor: pointer;
1673
+ font: 650 12px/1.2 var(--sans);
1674
+ }
1675
+ .trace-load-more:hover:not(:disabled) {
1676
+ border-color: var(--accent);
1677
+ color: var(--accent-strong);
1678
+ }
1679
+ .trace-load-more:disabled {
1680
+ cursor: default;
1681
+ opacity: 0.65;
1682
+ }
1683
+ .trace-entry {
1684
+ --trace-depth: 0;
1685
+ position: relative;
1686
+ display: grid;
1687
+ grid-template-columns: 100px minmax(0, 1fr);
1688
+ margin: 0 0 18px calc(var(--trace-depth) * 24px);
1689
+ }
1690
+ .trace-rail {
1691
+ position: relative;
1692
+ min-height: 36px;
1693
+ padding: 4px 28px 0 0;
1694
+ color: #8a8d95;
1695
+ text-align: right;
1696
+ font-family: var(--mono);
1697
+ }
1698
+ .trace-number,
1699
+ .trace-elapsed {
1700
+ display: block;
1701
+ white-space: nowrap;
1702
+ }
1703
+ .trace-number {
1704
+ font-size: 12px;
1705
+ font-weight: 650;
1706
+ }
1707
+ .trace-elapsed {
1708
+ margin-top: 3px;
1709
+ font-size: 10px;
1710
+ }
1711
+ .trace-dot {
1712
+ position: absolute;
1713
+ top: 10px;
1714
+ right: 11px;
1715
+ width: 11px;
1716
+ height: 11px;
1717
+ border: 2px solid var(--paper);
1718
+ border-radius: 50%;
1719
+ background: var(--accent);
1720
+ box-shadow: 0 0 0 1px #d7d9de;
1721
+ }
1722
+ .trace-card {
1723
+ min-width: 0;
1724
+ overflow: hidden;
1725
+ border: 1px solid #dddfe4;
1726
+ border-radius: 11px;
1727
+ background: rgba(255, 255, 255, 0.92);
1728
+ }
1729
+ .trace-card > header {
1730
+ display: flex;
1731
+ align-items: center;
1732
+ gap: 10px;
1733
+ min-height: 37px;
1734
+ padding: 8px 13px;
1735
+ border-bottom: 1px solid #eceef1;
1736
+ }
1737
+ .trace-status .trace-card > header {
1738
+ border-bottom: 0;
1739
+ padding-bottom: 5px;
1740
+ }
1741
+ .trace-kind {
1742
+ font-family: var(--mono);
1743
+ font-size: 10.5px;
1744
+ font-weight: 750;
1745
+ letter-spacing: 0.08em;
1746
+ text-transform: uppercase;
1747
+ }
1748
+ .trace-turn {
1749
+ color: var(--muted);
1750
+ font: 10px var(--mono);
1751
+ }
1752
+ .trace-status-badge {
1753
+ margin-left: auto;
1754
+ padding: 1px 6px;
1755
+ border-radius: 999px;
1756
+ background: #eef0f3;
1757
+ color: var(--muted);
1758
+ font: 9.5px var(--mono);
1759
+ text-transform: uppercase;
1760
+ }
1761
+ .trace-status-badge-error,
1762
+ .trace-status-badge-failed {
1763
+ background: #fef2f2;
1764
+ color: #b91c1c;
1765
+ }
1766
+ .trace-body {
1767
+ margin: 0;
1768
+ padding: 15px 17px 17px;
1769
+ overflow-wrap: anywhere;
1770
+ white-space: pre-wrap;
1771
+ font-family: var(--sans);
1772
+ font-size: 13px;
1773
+ line-height: 1.65;
1774
+ }
1775
+ .trace-reasoning .trace-card {
1776
+ border-style: dashed;
1777
+ border-color: #d7b98a;
1778
+ background: #fffdf8;
1779
+ }
1780
+ .trace-reasoning .trace-kind {
1781
+ color: #9a6b22;
1782
+ }
1783
+ .trace-reasoning .trace-body {
1784
+ font-style: italic;
1785
+ }
1786
+ .trace-user .trace-card {
1787
+ border-left: 3px solid #f3a66d;
1788
+ }
1789
+ .trace-tool_call .trace-card,
1790
+ .trace-tool_result .trace-card {
1791
+ border-color: #2d3036;
1792
+ background: #191a1e;
1793
+ color: #ececf0;
1794
+ }
1795
+ .trace-tool_call .trace-card > header,
1796
+ .trace-tool_result .trace-card > header {
1797
+ border-bottom-color: rgba(255, 255, 255, 0.1);
1798
+ }
1799
+ .trace-tool_call .trace-kind,
1800
+ .trace-tool_result .trace-kind {
1801
+ color: #f5a66d;
1802
+ }
1803
+ .trace-tool_call .trace-turn,
1804
+ .trace-tool_result .trace-turn {
1805
+ color: #979aa3;
1806
+ }
1807
+ .trace-tool_call .trace-body,
1808
+ .trace-tool_result .trace-body,
1809
+ .trace-output pre {
1810
+ font-family: var(--mono);
1811
+ font-size: 11.5px;
1812
+ line-height: 1.6;
1813
+ }
1814
+ #page .trace-tool_call pre.trace-body,
1815
+ #page .trace-tool_result pre.trace-body {
1816
+ margin: 0;
1817
+ padding: 15px 17px 17px;
1818
+ border: 0;
1819
+ border-radius: 0;
1820
+ background: transparent;
1821
+ color: #ececf0;
1822
+ }
1823
+ .trace-output {
1824
+ border-top: 1px dashed rgba(255, 255, 255, 0.14);
1825
+ }
1826
+ .trace-output summary {
1827
+ padding: 9px 14px;
1828
+ color: #aaaeb7;
1829
+ cursor: pointer;
1830
+ font: 700 10px var(--mono);
1831
+ letter-spacing: 0.06em;
1832
+ text-transform: uppercase;
1833
+ }
1834
+ #page .trace-output pre {
1835
+ max-height: 480px;
1836
+ margin: 0;
1837
+ padding: 0 16px 16px;
1838
+ border: 0;
1839
+ border-radius: 0;
1840
+ background: transparent;
1841
+ overflow: auto;
1842
+ color: #d7d8dd;
1843
+ white-space: pre-wrap;
1844
+ }
1845
+
1846
+ /* ---- workspace ---- */
1847
+ .workspace-header {
1848
+ padding-bottom: 24px;
1849
+ }
1850
+ .workspace-header p {
1851
+ margin: 0;
1852
+ color: var(--muted);
1853
+ font-family: var(--mono);
1854
+ font-size: 11px;
1855
+ }
1856
+ .workspace-inventory {
1857
+ overflow: hidden;
1858
+ border: 1px solid var(--line);
1859
+ border-radius: 11px;
1860
+ background: rgba(255, 255, 255, 0.92);
1861
+ }
1862
+ .workspace-folder > summary {
1863
+ display: flex;
1864
+ align-items: center;
1865
+ gap: 8px;
1866
+ min-height: 39px;
1867
+ padding: 8px 13px;
1868
+ background: #fafafa;
1869
+ cursor: pointer;
1870
+ font-weight: 650;
1871
+ list-style: none;
1872
+ }
1873
+ .workspace-folder > summary::-webkit-details-marker {
1874
+ display: none;
1875
+ }
1876
+ .workspace-folder > summary::after {
1877
+ content: "›";
1878
+ margin-left: auto;
1879
+ color: #989ba2;
1880
+ transform: rotate(90deg);
1881
+ }
1882
+ .workspace-folder:not([open]) > summary::after {
1883
+ transform: rotate(0);
1884
+ }
1885
+ .workspace-folder-children {
1886
+ padding-left: 20px;
1887
+ }
1888
+ .workspace-file {
1889
+ display: grid;
1890
+ grid-template-columns: minmax(180px, 1fr) 72px 78px 180px 36px;
1891
+ align-items: center;
1892
+ min-height: 44px;
1893
+ padding: 7px 10px 7px 13px;
1894
+ color: var(--muted);
1895
+ font-family: var(--mono);
1896
+ font-size: 10.5px;
1897
+ }
1898
+ .workspace-file-name {
1899
+ display: flex;
1900
+ align-items: center;
1901
+ min-width: 0;
1902
+ gap: 8px;
1903
+ color: var(--ink);
1904
+ font-family: var(--sans);
1905
+ font-size: 12.5px;
1906
+ font-weight: 550;
1907
+ }
1908
+ .workspace-file-name span {
1909
+ overflow: hidden;
1910
+ text-overflow: ellipsis;
1911
+ white-space: nowrap;
1912
+ }
1913
+ .workspace-file-type {
1914
+ width: fit-content;
1915
+ padding: 1px 6px;
1916
+ border-radius: 999px;
1917
+ background: var(--accent-soft);
1918
+ color: var(--accent-strong);
1919
+ text-transform: uppercase;
1920
+ }
1921
+ .workspace-download {
1922
+ display: inline-flex;
1923
+ align-items: center;
1924
+ justify-content: center;
1925
+ width: 30px;
1926
+ height: 30px;
1927
+ border-radius: 7px;
1928
+ color: var(--muted);
1929
+ }
1930
+ .workspace-download:hover {
1931
+ background: var(--accent-soft);
1932
+ color: var(--accent-strong);
1933
+ }
1934
+ .workspace-unpublished {
1935
+ color: #9ca3af;
1936
+ text-align: center;
1937
+ }
1938
+
1939
+ .workspace-header {
1940
+ display: flex;
1941
+ align-items: center;
1942
+ justify-content: space-between;
1943
+ gap: 16px;
1944
+ flex-wrap: wrap;
1945
+ }
1946
+ .workspace-toggle {
1947
+ display: inline-flex;
1948
+ align-items: center;
1949
+ padding: 2px;
1950
+ border: 1px solid var(--line);
1951
+ border-radius: 999px;
1952
+ background: #fafafa;
1953
+ }
1954
+ .workspace-toggle-btn {
1955
+ padding: 4px 13px;
1956
+ border: 0;
1957
+ border-radius: 999px;
1958
+ background: transparent;
1959
+ color: var(--muted);
1960
+ font-family: var(--sans);
1961
+ font-size: 12px;
1962
+ font-weight: 600;
1963
+ cursor: pointer;
1964
+ }
1965
+ .workspace-toggle-btn:hover {
1966
+ color: var(--accent-strong);
1967
+ }
1968
+ .workspace-toggle-btn.is-active {
1969
+ background: var(--accent);
1970
+ color: #ffffff;
1971
+ }
1972
+ .workspace-group + .workspace-group {
1973
+ margin-top: 18px;
1974
+ }
1975
+ .workspace-group-head,
1976
+ .workspace-hub-group-head {
1977
+ display: flex;
1978
+ align-items: center;
1979
+ gap: 8px;
1980
+ margin: 0;
1981
+ padding: 8px 13px;
1982
+ background: #fafafa;
1983
+ border-bottom: 1px solid var(--line);
1984
+ color: var(--ink);
1985
+ font-family: var(--sans);
1986
+ font-size: 12px;
1987
+ font-weight: 650;
1988
+ text-transform: capitalize;
1989
+ }
1990
+ .workspace-group-count,
1991
+ .workspace-hub-count {
1992
+ padding: 0 7px;
1993
+ border-radius: 999px;
1994
+ background: var(--accent-soft);
1995
+ color: var(--accent-strong);
1996
+ font-family: var(--mono);
1997
+ font-size: 10.5px;
1998
+ }
1999
+ .workspace-group {
2000
+ overflow: hidden;
2001
+ border: 1px solid var(--line);
2002
+ border-radius: 11px;
2003
+ background: rgba(255, 255, 255, 0.92);
2004
+ }
2005
+
2006
+ .workspace-hub {
2007
+ margin-top: 28px;
2008
+ }
2009
+ .workspace-hub-title {
2010
+ margin: 0 0 14px;
2011
+ font-family: var(--sans);
2012
+ font-size: 16px;
2013
+ font-weight: 700;
2014
+ color: var(--ink);
2015
+ }
2016
+ .workspace-hub-group {
2017
+ overflow: hidden;
2018
+ border: 1px solid var(--line);
2019
+ border-radius: 11px;
2020
+ background: rgba(255, 255, 255, 0.92);
2021
+ }
2022
+ .workspace-hub-group + .workspace-hub-group {
2023
+ margin-top: 14px;
2024
+ }
2025
+ .workspace-hub-list {
2026
+ display: flex;
2027
+ flex-direction: column;
2028
+ }
2029
+ .workspace-hub-link {
2030
+ padding: 9px 13px;
2031
+ color: var(--accent-strong);
2032
+ font-family: var(--mono);
2033
+ font-size: 12px;
2034
+ text-decoration: none;
2035
+ overflow: hidden;
2036
+ text-overflow: ellipsis;
2037
+ white-space: nowrap;
2038
+ }
2039
+ .workspace-hub-link + .workspace-hub-link {
2040
+ border-top: 1px solid var(--line);
2041
+ }
2042
+ .workspace-hub-link:hover {
2043
+ background: var(--accent-soft);
2044
+ text-decoration: underline;
2045
+ }
2046
+
2047
+ /* --- UI nits --- */
2048
+ /* Flush group headers: #page h3/h2 (ID selectors) otherwise inject a top margin
2049
+ that, with overflow:hidden on the card, shows as whitespace above "Jobs" etc. */
2050
+ #page .workspace-hub-title {
2051
+ margin: 0 0 14px;
2052
+ }
2053
+ #page .workspace-hub-group-head,
2054
+ #page .workspace-group-head {
2055
+ margin: 0;
2056
+ }
2057
+ /* HF brand logo before the "Hugging Face artifacts" heading */
2058
+ .workspace-hub-title {
2059
+ display: flex;
2060
+ align-items: center;
2061
+ gap: 9px;
2062
+ }
2063
+ .workspace-hub-logo {
2064
+ width: 22px;
2065
+ height: 22px;
2066
+ flex: none;
2067
+ }
2068
+ /* Center empty-state placeholders (heading, body, command) */
2069
+ .view-empty {
2070
+ display: flex;
2071
+ flex-direction: column;
2072
+ align-items: center;
2073
+ }
2074
+ #page .view-empty h2,
2075
+ #page .view-empty p {
2076
+ text-align: center;
2077
+ }
2078
+
2079
+ @media (max-width: 720px) {
2080
+ #app {
2081
+ flex-direction: column;
2082
+ }
2083
+ #sidebar {
2084
+ width: 100%;
2085
+ flex: none;
2086
+ height: auto;
2087
+ position: static;
2088
+ }
2089
+ #content {
2090
+ display: block;
2091
+ width: 100%;
2092
+ padding: 28px 20px 80px;
2093
+ overflow-x: hidden;
2094
+ }
2095
+ #view-tabs {
2096
+ margin: 0 0 20px;
2097
+ gap: 18px;
2098
+ justify-content: flex-start;
2099
+ overflow-x: auto;
2100
+ }
2101
+ #view-tabs a {
2102
+ flex: 0 0 auto;
2103
+ }
2104
+ .trace-timeline::before {
2105
+ left: 16px;
2106
+ }
2107
+ .trace-entry {
2108
+ grid-template-columns: 32px minmax(0, 1fr);
2109
+ margin-left: calc(var(--trace-depth) * 10px);
2110
+ }
2111
+ .trace-rail {
2112
+ padding: 0;
2113
+ }
2114
+ .trace-number,
2115
+ .trace-elapsed {
2116
+ display: none;
2117
+ }
2118
+ .trace-dot {
2119
+ top: 10px;
2120
+ right: 10px;
2121
+ }
2122
+ .workspace-file {
2123
+ grid-template-columns: minmax(150px, 1fr) 66px 34px;
2124
+ }
2125
+ .workspace-file-size,
2126
+ .workspace-file-time {
2127
+ display: none;
2128
+ }
2129
+ #page {
2130
+ width: 100%;
2131
+ max-width: 100%;
2132
+ }
2133
+ #page h1,
2134
+ #logbook-title {
2135
+ font-size: 30px;
2136
+ }
2137
+ .cell-head {
2138
+ align-items: flex-start;
2139
+ flex-direction: column;
2140
+ gap: 4px;
2141
+ }
2142
+ }
logbook.js ADDED
The diff for this file is too large to render. See raw diff
 
logbook.json ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema_version": 2,
3
+ "title": "Reproduction: Distributed Direct Preference Optimization",
4
+ "emoji": "🎯",
5
+ "space_id": "SabaPivot/repro-distributed-direct-preference-optimization",
6
+ "paper": {
7
+ "title": "Distributed Direct Preference Optimization",
8
+ "openreview_id": "ljNZyrAlaa",
9
+ "arxiv_id": "2605.20696",
10
+ "url": "https://openreview.net/forum?id=ljNZyrAlaa"
11
+ },
12
+ "tags": [
13
+ "trackio",
14
+ "open-reproductions",
15
+ "icml2026",
16
+ "icml2026-repro",
17
+ "paper-ljNZyrAlaa"
18
+ ],
19
+ "updated_at": "2026-07-29T14:48:30+00:00",
20
+ "root": {
21
+ "slug": "index",
22
+ "title": "Reproduction: Distributed Direct Preference Optimization",
23
+ "file": "pages/index.md",
24
+ "children": [
25
+ {
26
+ "slug": "executive-summary",
27
+ "title": "Executive summary",
28
+ "file": "pages/executive-summary/page.md",
29
+ "children": []
30
+ },
31
+ {
32
+ "slug": "claim-1-feddpo-partial-participation-bound",
33
+ "title": "Claim 1: FedDPO partial participation bound",
34
+ "file": "pages/claim-1-feddpo-partial-participation-bound/page.md",
35
+ "children": []
36
+ },
37
+ {
38
+ "slug": "claim-2-full-participation-corollary",
39
+ "title": "Claim 2: Full participation corollary",
40
+ "file": "pages/claim-2-full-participation-corollary/page.md",
41
+ "children": []
42
+ },
43
+ {
44
+ "slug": "claim-3-staleness-penalty",
45
+ "title": "Claim 3: Staleness penalty",
46
+ "file": "pages/claim-3-staleness-penalty/page.md",
47
+ "children": []
48
+ },
49
+ {
50
+ "slug": "claim-4-feddpo-lower-bound",
51
+ "title": "Claim 4: FedDPO lower bound",
52
+ "file": "pages/claim-4-feddpo-lower-bound/page.md",
53
+ "children": []
54
+ },
55
+ {
56
+ "slug": "claim-5-decdpo-spectral-rate",
57
+ "title": "Claim 5: DecDPO spectral rate",
58
+ "file": "pages/claim-5-decdpo-spectral-rate/page.md",
59
+ "children": []
60
+ },
61
+ {
62
+ "slug": "claim-6-shp-numerical-results",
63
+ "title": "Claim 6: SHP numerical results",
64
+ "file": "pages/claim-6-shp-numerical-results/page.md",
65
+ "children": []
66
+ },
67
+ {
68
+ "slug": "conclusion",
69
+ "title": "Conclusion",
70
+ "file": "pages/conclusion/page.md",
71
+ "children": []
72
+ }
73
+ ]
74
+ },
75
+ "traces": [],
76
+ "workspace": {
77
+ "file": "workspace.json",
78
+ "file_count": 0,
79
+ "total_size": 0,
80
+ "bucket_id": null
81
+ },
82
+ "agent_view_tokens": 5866,
83
+ "trace_view_tokens": 17229,
84
+ "workspace_view_tokens": 85,
85
+ "revision": "e6d12b1ed894120a273d",
86
+ "traces_ref": {
87
+ "repo_id": "SabaPivot/repro-distributed-direct-preference-optimization-traces",
88
+ "repo_type": "dataset",
89
+ "repo_url": "https://huggingface.co/datasets/SabaPivot/repro-distributed-direct-preference-optimization-traces",
90
+ "private": true,
91
+ "viewer_path": "trackio/index.json"
92
+ },
93
+ "trace_dataset": "https://huggingface.co/datasets/SabaPivot/repro-distributed-direct-preference-optimization-traces",
94
+ "workspace_ref": {
95
+ "repo_id": "SabaPivot/repro-distributed-direct-preference-optimization-artifacts",
96
+ "repo_type": "bucket",
97
+ "repo_url": "https://huggingface.co/buckets/SabaPivot/repro-distributed-direct-preference-optimization-artifacts",
98
+ "private": true
99
+ },
100
+ "workspace_bucket": "https://huggingface.co/buckets/SabaPivot/repro-distributed-direct-preference-optimization-artifacts"
101
+ }
pages/claim-1-feddpo-partial-participation-bound/page.md ADDED
@@ -0,0 +1,835 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Claim 1: FedDPO partial participation bound
2
+
3
+
4
+ ---
5
+ <!-- trackio-cell
6
+ {"type": "markdown", "id": "cell_96cda8a2dfb6", "created_at": "2026-07-29T13:20:39+00:00", "title": "Test plan"}
7
+ -->
8
+ This page tests whether Theorem 5.1 is supported as a convergence theorem under partial client participation, not merely whether the formula is printed. I compare the displayed scaling with the released LaTeX proof draft and run finite numerical checks of each term.
9
+
10
+
11
+ ---
12
+ <!-- trackio-cell
13
+ {"type": "code", "id": "cell_abd13ff179d8", "created_at": "2026-07-29T13:15:53+00:00", "title": "Run: uv theory_audit.py (exit 0)", "command": ["uv", "run", "theory_audit.py", "--output", "theory_outputs"], "exit_code": 0, "duration_s": 5.768}
14
+ -->
15
+ ````bash
16
+ $ uv run theory_audit.py --output theory_outputs
17
+ ````
18
+
19
+ exit 0 · 5.8s
20
+
21
+
22
+ ````python title=theory_audit.py
23
+ # /// script
24
+ # requires-python = ">=3.11"
25
+ # dependencies = [
26
+ # "numpy>=2.0",
27
+ # "plotly>=6.0",
28
+ # "trackio>=0.33.0",
29
+ # ]
30
+ # ///
31
+ """Independent numerical and consistency audit of the five theory claims.
32
+
33
+ This is not a replacement for a proof. It checks the displayed bounds,
34
+ closed-form spectral recursions, and two logical/proof obligations exposed by
35
+ the paper source.
36
+ """
37
+
38
+ from __future__ import annotations
39
+
40
+ import argparse
41
+ import csv
42
+ import json
43
+ import math
44
+ from pathlib import Path
45
+
46
+ import numpy as np
47
+ import plotly.graph_objects as go
48
+ from plotly.subplots import make_subplots
49
+ import trackio
50
+
51
+
52
+ def partial_bound(
53
+ *,
54
+ delta: float,
55
+ eta: float,
56
+ e: int,
57
+ rounds: int,
58
+ clients: int,
59
+ smoothness: float,
60
+ variance: float,
61
+ heterogeneity: float,
62
+ ) -> dict[str, float]:
63
+ terms = {
64
+ "optimization": 2.0 * delta / (eta * e * rounds),
65
+ "variance_1_over_s": 8.0 * smoothness * eta * variance / clients,
66
+ "heterogeneity_drift": (
67
+ 16.0 * smoothness**2 * eta**2 * e * heterogeneity
68
+ ),
69
+ "local_variance": (
70
+ 16.0 * smoothness**2 * eta**2 * e**2 * variance / clients
71
+ ),
72
+ }
73
+ terms["total"] = sum(terms.values())
74
+ return terms
75
+
76
+
77
+ def full_bound(
78
+ *,
79
+ delta: float,
80
+ eta: float,
81
+ e: int,
82
+ rounds: int,
83
+ n_clients: int,
84
+ smoothness: float,
85
+ variance: float,
86
+ heterogeneity: float,
87
+ ) -> dict[str, float]:
88
+ terms = {
89
+ "optimization": 2.0 * delta / (eta * e * rounds),
90
+ "averaged_stochastic_variance_1_over_n": (
91
+ 2.0 * smoothness * eta * variance / n_clients
92
+ ),
93
+ "heterogeneity_drift": (
94
+ 8.0 * smoothness**2 * eta**2 * e * heterogeneity
95
+ ),
96
+ }
97
+ terms["total"] = sum(terms.values())
98
+ return terms
99
+
100
+
101
+ def mixing_matrix(graph_type: str, n: int) -> np.ndarray:
102
+ edges: list[tuple[int, int]]
103
+ if graph_type == "path":
104
+ edges = [(i, i + 1) for i in range(n - 1)]
105
+ elif graph_type == "ring":
106
+ edges = [(i, (i + 1) % n) for i in range(n)]
107
+ elif graph_type == "star":
108
+ edges = [(0, i) for i in range(1, n)]
109
+ elif graph_type == "complete":
110
+ edges = [(i, j) for i in range(n) for j in range(i + 1, n)]
111
+ elif graph_type == "disconnected":
112
+ edges = [(0, 1), (2, 3)]
113
+ else:
114
+ raise ValueError(graph_type)
115
+
116
+ degree = np.zeros(n, dtype=int)
117
+ for i, j in edges:
118
+ degree[i] += 1
119
+ degree[j] += 1
120
+ pi = np.zeros((n, n), dtype=np.float64)
121
+ for i, j in edges:
122
+ weight = 1.0 / (1 + max(degree[i], degree[j]))
123
+ pi[i, j] = weight
124
+ pi[j, i] = weight
125
+ for i in range(n):
126
+ pi[i, i] = 1.0 - pi[i].sum()
127
+ return pi
128
+
129
+
130
+ def spectral_row(graph_type: str, n: int, eta: float, injection: float) -> dict:
131
+ pi = mixing_matrix(graph_type, n)
132
+ eigenvalues = np.linalg.eigvalsh(pi)
133
+ ordered = np.sort(np.abs(eigenvalues))[::-1]
134
+ rho = float(ordered[1])
135
+ gap = 1.0 - rho**2
136
+
137
+ if gap > 1e-12:
138
+ closed_form = eta**2 * injection / gap
139
+ error = 0.0
140
+ for _ in range(100_000):
141
+ next_error = rho**2 * error + eta**2 * injection
142
+ if abs(next_error - error) < 1e-15:
143
+ error = next_error
144
+ break
145
+ error = next_error
146
+ relative_error = abs(error - closed_form) / max(abs(closed_form), 1e-30)
147
+ else:
148
+ closed_form = math.inf
149
+ error = 1000 * eta**2 * injection
150
+ relative_error = math.nan
151
+
152
+ return {
153
+ "topology": graph_type,
154
+ "rho": rho,
155
+ "one_minus_rho_sq": gap,
156
+ "inverse_gap": math.inf if gap <= 1e-12 else 1.0 / gap,
157
+ "recursion_limit_numeric": error,
158
+ "recursion_limit_closed_form": closed_form,
159
+ "relative_error": relative_error,
160
+ "row_sum_max_error": float(np.max(np.abs(pi.sum(axis=1) - 1.0))),
161
+ "symmetry_max_error": float(np.max(np.abs(pi - pi.T))),
162
+ }
163
+
164
+
165
+ def write_csv(path: Path, rows: list[dict]) -> None:
166
+ keys = list(rows[0])
167
+ with path.open("w", newline="", encoding="utf-8") as handle:
168
+ writer = csv.DictWriter(handle, fieldnames=keys)
169
+ writer.writeheader()
170
+ writer.writerows(rows)
171
+
172
+
173
+ def main() -> None:
174
+ parser = argparse.ArgumentParser()
175
+ parser.add_argument("--output", type=Path, default=Path("theory_outputs"))
176
+ args = parser.parse_args()
177
+ args.output.mkdir(parents=True, exist_ok=True)
178
+
179
+ constants = {
180
+ "delta": 1.0,
181
+ "eta": 0.01,
182
+ "rounds": 1000,
183
+ "n_clients": 5,
184
+ "smoothness": 1.0,
185
+ "variance": 0.4,
186
+ "heterogeneity": 0.2,
187
+ }
188
+ trackio.init(
189
+ project="ddpo-theory-audit",
190
+ name="closed-form-and-recursion",
191
+ config=constants,
192
+ )
193
+
194
+ bound_rows: list[dict] = []
195
+ for e in [1, 3, 6, 12]:
196
+ for clients in [1, 3, 5]:
197
+ terms = partial_bound(
198
+ delta=constants["delta"],
199
+ eta=constants["eta"],
200
+ e=e,
201
+ rounds=constants["rounds"],
202
+ clients=clients,
203
+ smoothness=constants["smoothness"],
204
+ variance=constants["variance"],
205
+ heterogeneity=constants["heterogeneity"],
206
+ )
207
+ bound_rows.append({"E": e, "S": clients, **terms})
208
+
209
+ partial_at_full = partial_bound(
210
+ delta=constants["delta"],
211
+ eta=constants["eta"],
212
+ e=3,
213
+ rounds=constants["rounds"],
214
+ clients=constants["n_clients"],
215
+ smoothness=constants["smoothness"],
216
+ variance=constants["variance"],
217
+ heterogeneity=constants["heterogeneity"],
218
+ )
219
+ tightened_full = full_bound(
220
+ delta=constants["delta"],
221
+ eta=constants["eta"],
222
+ e=3,
223
+ rounds=constants["rounds"],
224
+ n_clients=constants["n_clients"],
225
+ smoothness=constants["smoothness"],
226
+ variance=constants["variance"],
227
+ heterogeneity=constants["heterogeneity"],
228
+ )
229
+
230
+ eta = constants["eta"]
231
+ e = 3
232
+ cq = eta**2 * e * (
233
+ constants["heterogeneity"] + constants["variance"]
234
+ )
235
+ staleness_rows = [
236
+ {
237
+ "q_max": q,
238
+ "C_q": cq,
239
+ "eta_C_q_q_max": eta * cq * q,
240
+ }
241
+ for q in [0, 1, 2, 5, 10]
242
+ ]
243
+
244
+ lower_bound_rows = [
245
+ {
246
+ "E_budget": e_budget,
247
+ "claimed_scale_E_kappa_sq_over_S": (
248
+ e_budget
249
+ * constants["heterogeneity"]
250
+ / 3.0
251
+ ),
252
+ "one_step_algorithm_updates_used": 1,
253
+ "one_step_algorithm_is_admissible": True,
254
+ }
255
+ for e_budget in [1, 2, 4, 8, 16]
256
+ ]
257
+
258
+ spectral_rows = [
259
+ spectral_row(graph, 5, eta=0.01, injection=0.6)
260
+ for graph in ["path", "ring", "star", "complete", "disconnected"]
261
+ ]
262
+ connected_errors = [
263
+ row["relative_error"]
264
+ for row in spectral_rows
265
+ if row["topology"] != "disconnected"
266
+ ]
267
+
268
+ proof_obligations = {
269
+ "claim_1_second_moment_counterexample": {
270
+ "description": (
271
+ "The commented proof claims ||sum over S identical gradients||^2 "
272
+ "<= S times the population second moment."
273
+ ),
274
+ "S": 3,
275
+ "identical_gradient_norm": 1.0,
276
+ "claimed_lhs": 9.0,
277
+ "claimed_rhs": 3.0,
278
+ "inequality_holds": False,
279
+ },
280
+ "claim_1_constant_reshaping_counterexample": {
281
+ "description": (
282
+ "The commented proof attempts to absorb a non-vanishing "
283
+ "heterogeneity term into O(eta^2 E kappa^2) by choosing eta small."
284
+ ),
285
+ "eta_values": [1e-1, 1e-2, 1e-3, 1e-4],
286
+ "fixed_source_term_2_kappa_sq": 2 * constants["heterogeneity"],
287
+ "target_terms_16_eta_sq_E_kappa_sq": [
288
+ 16 * value**2 * 3 * constants["heterogeneity"]
289
+ for value in [1e-1, 1e-2, 1e-3, 1e-4]
290
+ ],
291
+ "absorption_improves_as_eta_shrinks": False,
292
+ },
293
+ "claim_2_literal_variance_vanishes": {
294
+ "partial_bound_at_S_equals_N": partial_at_full,
295
+ "tightened_full_bound": tightened_full,
296
+ "full_bound_has_nonzero_1_over_N_stochastic_variance": (
297
+ tightened_full["averaged_stochastic_variance_1_over_n"] > 0
298
+ ),
299
+ },
300
+ "claim_4_budget_class_nesting": {
301
+ "description": (
302
+ "An algorithm that always uses one update is admissible for every "
303
+ "'at most E' budget. The admissible class expands with E, so its "
304
+ "minimax optimum cannot worsen solely because E is larger."
305
+ ),
306
+ "claimed_scale_is_increasing": True,
307
+ "admissible_class_is_nested": True,
308
+ },
309
+ "claim_5_recursion_double_precision": {
310
+ "max_relative_error_connected": max(connected_errors),
311
+ "all_connected_checks_below_1e-10": max(connected_errors) < 1e-10,
312
+ "disconnected_control_has_zero_gap": (
313
+ spectral_rows[-1]["one_minus_rho_sq"] <= 1e-12
314
+ ),
315
+ },
316
+ }
317
+
318
+ write_csv(args.output / "bound_terms.csv", bound_rows)
319
+ write_csv(args.output / "staleness_terms.csv", staleness_rows)
320
+ write_csv(args.output / "lower_bound_budget.csv", lower_bound_rows)
321
+ write_csv(args.output / "spectral_recursion.csv", spectral_rows)
322
+
323
+ report = {
324
+ "scope": "numerical/consistency audit, not a proof",
325
+ "constants": constants,
326
+ "partial_bound_rows": bound_rows,
327
+ "staleness_rows": staleness_rows,
328
+ "lower_bound_rows": lower_bound_rows,
329
+ "spectral_rows": spectral_rows,
330
+ "proof_obligations": proof_obligations,
331
+ }
332
+ report_path = args.output / "theory_audit.json"
333
+ report_path.write_text(json.dumps(report, indent=2), encoding="utf-8")
334
+
335
+ fig = make_subplots(
336
+ rows=2,
337
+ cols=2,
338
+ subplot_titles=(
339
+ "Displayed FedDPO bound vs local steps",
340
+ "Displayed FedDPO bound vs participation",
341
+ "Staleness term is linear by definition",
342
+ "Consensus recursion vs spectral gap",
343
+ ),
344
+ )
345
+ for clients in [1, 3, 5]:
346
+ rows = [row for row in bound_rows if row["S"] == clients]
347
+ fig.add_trace(
348
+ go.Scatter(
349
+ x=[row["E"] for row in rows],
350
+ y=[row["total"] for row in rows],
351
+ mode="lines+markers",
352
+ name=f"S={clients}",
353
+ ),
354
+ row=1,
355
+ col=1,
356
+ )
357
+ for e_value in [1, 3, 6, 12]:
358
+ rows = [row for row in bound_rows if row["E"] == e_value]
359
+ fig.add_trace(
360
+ go.Scatter(
361
+ x=[1.0 / row["S"] for row in rows],
362
+ y=[row["total"] for row in rows],
363
+ mode="lines+markers",
364
+ name=f"E={e_value}",
365
+ ),
366
+ row=1,
367
+ col=2,
368
+ )
369
+ fig.add_trace(
370
+ go.Scatter(
371
+ x=[row["q_max"] for row in staleness_rows],
372
+ y=[row["eta_C_q_q_max"] for row in staleness_rows],
373
+ mode="lines+markers",
374
+ name="η Cq qmax",
375
+ ),
376
+ row=2,
377
+ col=1,
378
+ )
379
+ connected = [
380
+ row for row in spectral_rows if row["topology"] != "disconnected"
381
+ ]
382
+ fig.add_trace(
383
+ go.Scatter(
384
+ x=[row["inverse_gap"] for row in connected],
385
+ y=[row["recursion_limit_numeric"] for row in connected],
386
+ mode="markers+text",
387
+ text=[row["topology"] for row in connected],
388
+ textposition="top center",
389
+ name="numeric fixed point",
390
+ ),
391
+ row=2,
392
+ col=2,
393
+ )
394
+ fig.update_layout(
395
+ title="Distributed DPO theory: statement-level numerical audit",
396
+ template="plotly_white",
397
+ height=800,
398
+ width=1200,
399
+ )
400
+ fig.update_xaxes(title_text="E", row=1, col=1)
401
+ fig.update_xaxes(title_text="1/S", row=1, col=2)
402
+ fig.update_xaxes(title_text="q_max", row=2, col=1)
403
+ fig.update_xaxes(title_text="1 / (1 - ρ²)", row=2, col=2)
404
+ fig.update_yaxes(title_text="bound", row=1, col=1)
405
+ fig.update_yaxes(title_text="bound", row=1, col=2)
406
+ fig.update_yaxes(title_text="penalty", row=2, col=1)
407
+ fig.update_yaxes(title_text="steady-state error", row=2, col=2)
408
+ figure_path = args.output / "theory_audit.html"
409
+ fig.write_html(figure_path, include_plotlyjs="cdn")
410
+
411
+ for index, row in enumerate(bound_rows):
412
+ trackio.log(
413
+ {
414
+ "bound/total": row["total"],
415
+ "bound/E": row["E"],
416
+ "bound/S": row["S"],
417
+ },
418
+ step=index,
419
+ )
420
+ trackio.log(
421
+ {
422
+ "checks/max_spectral_relative_error": max(connected_errors),
423
+ "checks/claim1_identical_gradient_lhs_over_rhs": 3.0,
424
+ "checks/full_variance_term": tightened_full[
425
+ "averaged_stochastic_variance_1_over_n"
426
+ ],
427
+ },
428
+ step=len(bound_rows),
429
+ )
430
+ trackio.log_artifact(
431
+ args.output,
432
+ name="ddpo-theory-audit",
433
+ type="dataset",
434
+ aliases=["reproduction"],
435
+ )
436
+ trackio.finish()
437
+
438
+ print(json.dumps(
439
+ {
440
+ "report": str(report_path),
441
+ "figure": str(figure_path),
442
+ "max_spectral_relative_error": max(connected_errors),
443
+ "claim1_identical_gradient_counterexample": "9 > 3",
444
+ "full_bound_variance_term": tightened_full[
445
+ "averaged_stochastic_variance_1_over_n"
446
+ ],
447
+ },
448
+ indent=2,
449
+ ))
450
+
451
+
452
+ if __name__ == "__main__":
453
+ main()
454
+
455
+ ````
456
+
457
+
458
+ ````output
459
+ Installed 31 packages in 161ms
460
+ * Trackio project initialized: ddpo-theory-audit
461
+ * Trackio metrics logged to: /Users/test/.cache/huggingface/trackio
462
+ * View dashboard by running in your terminal:
463
+ trackio show --project "ddpo-theory-audit"
464
+ * or by running in Python: trackio.show(project="ddpo-theory-audit")
465
+ * Created new run: closed-form-and-recursion
466
+ * Run finished. Uploading logs to Trackio (please wait...)
467
+ {
468
+ "report": "theory_outputs/theory_audit.json",
469
+ "figure": "theory_outputs/theory_audit.html",
470
+ "max_spectral_relative_error": 9.973519694151137e-12,
471
+ "claim1_identical_gradient_counterexample": "9 > 3",
472
+ "full_bound_variance_term": 0.0016
473
+ }
474
+
475
+ ````
476
+
477
+
478
+ ---
479
+ <!-- trackio-cell
480
+ {"type": "artifact", "id": "cell_2edb6d18dcf1", "created_at": "2026-07-29T13:15:53+00:00", "title": "Artifact: bound_terms.csv", "path": "theory_outputs/bound_terms.csv", "size": 1128, "artifact_type": "dataset", "auto": true}
481
+ -->
482
+ **📦 Artifact** `theory_outputs/bound_terms.csv` · dataset · 1.1 kB
483
+
484
+ https://huggingface.co/buckets/SabaPivot/repro-distributed-direct-preference-optimization-artifacts#logbook-files/theory_outputs/bound_terms.csv
485
+
486
+
487
+ ---
488
+ <!-- trackio-cell
489
+ {"type": "artifact", "id": "cell_b16da1f88862", "created_at": "2026-07-29T13:15:53+00:00", "title": "Artifact: spectral_recursion.csv", "path": "theory_outputs/spectral_recursion.csv", "size": 656, "artifact_type": "dataset", "auto": true}
490
+ -->
491
+ **📦 Artifact** `theory_outputs/spectral_recursion.csv` · dataset · 656 B
492
+
493
+ https://huggingface.co/buckets/SabaPivot/repro-distributed-direct-preference-optimization-artifacts#logbook-files/theory_outputs/spectral_recursion.csv
494
+
495
+
496
+ ---
497
+ <!-- trackio-cell
498
+ {"type": "artifact", "id": "cell_6e2e147dd868", "created_at": "2026-07-29T13:15:53+00:00", "title": "Artifact: lower_bound_budget.csv", "path": "theory_outputs/lower_bound_budget.csv", "size": 256, "artifact_type": "dataset", "auto": true}
499
+ -->
500
+ **📦 Artifact** `theory_outputs/lower_bound_budget.csv` · dataset · 256 B
501
+
502
+ https://huggingface.co/buckets/SabaPivot/repro-distributed-direct-preference-optimization-artifacts#logbook-files/theory_outputs/lower_bound_budget.csv
503
+
504
+
505
+ ---
506
+ <!-- trackio-cell
507
+ {"type": "artifact", "id": "cell_c1c87f178c3b", "created_at": "2026-07-29T13:15:53+00:00", "title": "Artifact: staleness_terms.csv", "path": "theory_outputs/staleness_terms.csv", "size": 251, "artifact_type": "dataset", "auto": true}
508
+ -->
509
+ **📦 Artifact** `theory_outputs/staleness_terms.csv` · dataset · 251 B
510
+
511
+ https://huggingface.co/buckets/SabaPivot/repro-distributed-direct-preference-optimization-artifacts#logbook-files/theory_outputs/staleness_terms.csv
512
+
513
+
514
+ ---
515
+ <!-- trackio-cell
516
+ {"type": "markdown", "id": "cell_5f113aecb41a", "created_at": "2026-07-29T13:20:40+00:00", "title": "Finding"}
517
+ -->
518
+ **Verdict: statement present, theorem not verified.** Theorem 5.1 displays
519
+
520
+ `avg_r E||∇L(θ_r)||² ≤ 2Δ/(ηER) + 8Lηζ²_g/S + 16L²η²Eκ² + 16L²η²E²ζ²_g/S`
521
+
522
+ under `η ≤ min{1/(8LE), S/(16LNE)}`. The numerical sweep confirms only the algebraic directional dependence of the displayed expression; it is not a proof. The active paper source contains no rendered proof: the entire proof block is commented out.
523
+
524
+ Two independent blockers appear in that commented draft. First, its Eq. 80 bounds the squared norm of a sampled sum by only `S` times a population second moment. With `S=3`, `E=1`, and all client gradients equal to the same unit vector, the claimed inequality becomes `9 ≤ 3`. Second, draft Eq. 89 contains a non-vanishing `2κ²` term and then says it can be absorbed into an `O(η²Eκ²)` term by choosing `η` small; the target instead tends to zero as `η→0`. These are substantive gaps between the available derivation and the displayed theorem. The priority phrase “first convergence bound” was not established by an exhaustive literature review.
525
+
526
+ Sources: [arXiv 2605.20696](https://arxiv.org/abs/2605.20696), [OpenReview ljNZyrAlaa](https://openreview.net/forum?id=ljNZyrAlaa), [official repository at commit 97ef67e](https://github.com/starkjiang/distributed_dpo/tree/97ef67ef9e44df8b75aeef55e4ecaa6141d6ade7).
527
+
528
+
529
+ ---
530
+ <!-- trackio-cell
531
+ {"type": "figure", "id": "cell_186d7c55c977", "created_at": "2026-07-29T13:22:51+00:00", "title": "Bound and proof-obligation audit"}
532
+ -->
533
+ ````html
534
+ <html>
535
+ <head><meta charset="utf-8" /></head>
536
+ <body>
537
+ <div style="height:800px; width:1200px;"> <script>window.PlotlyConfig = {MathJaxConfig: 'local'};</script>
538
+ <script charset="utf-8" src="https://cdn.plot.ly/plotly-3.7.0.min.js" integrity="sha256-jvTGqxNp8AGWEcvNLVuKr+8j5dGe9Yw51LQkmDH+IYA=" crossorigin="anonymous"></script> <div id="2cfc58e2-9925-4649-9835-b357bd7e6f2e" class="plotly-graph-div" style="height:100%; width:100%;"></div> <script> window.PLOTLYENV=window.PLOTLYENV || {}; if (document.getElementById("2cfc58e2-9925-4649-9835-b357bd7e6f2e")) { Plotly.newPlot( "2cfc58e2-9925-4649-9835-b357bd7e6f2e", [{"mode":"lines+markers","name":"S=1","x":[1,3,6,12],"y":[0.23296,0.10538666666666667,0.09029333333333334,0.1446666666666667],"type":"scatter","xaxis":"x","yaxis":"y"},{"mode":"lines+markers","name":"S=3","x":[1,3,6,12],"y":[0.2112,0.08021333333333333,0.0536,0.06189333333333334],"type":"scatter","xaxis":"x","yaxis":"y"},{"mode":"lines+markers","name":"S=5","x":[1,3,6,12],"y":[0.206848,0.07517866666666667,0.046261333333333335,0.04533866666666667],"type":"scatter","xaxis":"x","yaxis":"y"},{"mode":"lines+markers","name":"E=1","x":[1.0,0.3333333333333333,0.2],"y":[0.23296,0.2112,0.206848],"type":"scatter","xaxis":"x2","yaxis":"y2"},{"mode":"lines+markers","name":"E=3","x":[1.0,0.3333333333333333,0.2],"y":[0.10538666666666667,0.08021333333333333,0.07517866666666667],"type":"scatter","xaxis":"x2","yaxis":"y2"},{"mode":"lines+markers","name":"E=6","x":[1.0,0.3333333333333333,0.2],"y":[0.09029333333333334,0.0536,0.046261333333333335],"type":"scatter","xaxis":"x2","yaxis":"y2"},{"mode":"lines+markers","name":"E=12","x":[1.0,0.3333333333333333,0.2],"y":[0.1446666666666667,0.06189333333333334,0.04533866666666667],"type":"scatter","xaxis":"x2","yaxis":"y2"},{"mode":"lines+markers","name":"η Cq qmax","x":[0,1,2,5,10],"y":[0.0,1.8000000000000003e-6,3.6000000000000007e-6,9.000000000000002e-6,0.000018000000000000004],"type":"scatter","xaxis":"x3","yaxis":"y3"},{"mode":"markers+text","name":"numeric fixed point","text":["path","ring","star","complete"],"textposition":"top center","x":[4.194048299802485,1.4102237436136194,2.7777777777777786,1.0],"y":[0.0002516428979856393,0.00008461342461668239,0.00016666666666517355,0.00006],"type":"scatter","xaxis":"x4","yaxis":"y4"}], {"template":{"data":{"barpolar":[{"marker":{"line":{"color":"white","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"barpolar"}],"bar":[{"error_x":{"color":"#2a3f5f"},"error_y":{"color":"#2a3f5f"},"marker":{"line":{"color":"white","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"bar"}],"carpet":[{"aaxis":{"endlinecolor":"#2a3f5f","gridcolor":"#C8D4E3","linecolor":"#C8D4E3","minorgridcolor":"#C8D4E3","startlinecolor":"#2a3f5f"},"baxis":{"endlinecolor":"#2a3f5f","gridcolor":"#C8D4E3","linecolor":"#C8D4E3","minorgridcolor":"#C8D4E3","startlinecolor":"#2a3f5f"},"type":"carpet"}],"choropleth":[{"colorbar":{"outlinewidth":0,"ticks":""},"type":"choropleth"}],"contourcarpet":[{"colorbar":{"outlinewidth":0,"ticks":""},"type":"contourcarpet"}],"contour":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"contour"}],"heatmap":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"heatmap"}],"histogram2dcontour":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"histogram2dcontour"}],"histogram2d":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"histogram2d"}],"histogram":[{"marker":{"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"histogram"}],"mesh3d":[{"colorbar":{"outlinewidth":0,"ticks":""},"type":"mesh3d"}],"parcoords":[{"line":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"parcoords"}],"pie":[{"automargin":true,"type":"pie"}],"scatter3d":[{"line":{"colorbar":{"outlinewidth":0,"ticks":""}},"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatter3d"}],"scattercarpet":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattercarpet"}],"scattergeo":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattergeo"}],"scattergl":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattergl"}],"scattermapbox":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattermapbox"}],"scattermap":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattermap"}],"scatterpolargl":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatterpolargl"}],"scatterpolar":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatterpolar"}],"scatter":[{"fillpattern":{"fillmode":"overlay","size":10,"solidity":0.2},"type":"scatter"}],"scatterternary":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatterternary"}],"surface":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"surface"}],"table":[{"cells":{"fill":{"color":"#EBF0F8"},"line":{"color":"white"}},"header":{"fill":{"color":"#C8D4E3"},"line":{"color":"white"}},"type":"table"}]},"layout":{"annotationdefaults":{"arrowcolor":"#2a3f5f","arrowhead":0,"arrowwidth":1},"autotypenumbers":"strict","coloraxis":{"colorbar":{"outlinewidth":0,"ticks":""}},"colorscale":{"diverging":[[0,"#8e0152"],[0.1,"#c51b7d"],[0.2,"#de77ae"],[0.3,"#f1b6da"],[0.4,"#fde0ef"],[0.5,"#f7f7f7"],[0.6,"#e6f5d0"],[0.7,"#b8e186"],[0.8,"#7fbc41"],[0.9,"#4d9221"],[1,"#276419"]],"sequential":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"sequentialminus":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]},"colorway":["#636efa","#EF553B","#00cc96","#ab63fa","#FFA15A","#19d3f3","#FF6692","#B6E880","#FF97FF","#FECB52"],"font":{"color":"#2a3f5f"},"geo":{"bgcolor":"white","lakecolor":"white","landcolor":"white","showlakes":true,"showland":true,"subunitcolor":"#C8D4E3"},"hoverlabel":{"align":"left"},"hovermode":"closest","mapbox":{"style":"light"},"paper_bgcolor":"white","plot_bgcolor":"white","polar":{"angularaxis":{"gridcolor":"#EBF0F8","linecolor":"#EBF0F8","ticks":""},"bgcolor":"white","radialaxis":{"gridcolor":"#EBF0F8","linecolor":"#EBF0F8","ticks":""}},"scene":{"xaxis":{"backgroundcolor":"white","gridcolor":"#DFE8F3","gridwidth":2,"linecolor":"#EBF0F8","showbackground":true,"ticks":"","zerolinecolor":"#EBF0F8"},"yaxis":{"backgroundcolor":"white","gridcolor":"#DFE8F3","gridwidth":2,"linecolor":"#EBF0F8","showbackground":true,"ticks":"","zerolinecolor":"#EBF0F8"},"zaxis":{"backgroundcolor":"white","gridcolor":"#DFE8F3","gridwidth":2,"linecolor":"#EBF0F8","showbackground":true,"ticks":"","zerolinecolor":"#EBF0F8"}},"shapedefaults":{"line":{"color":"#2a3f5f"}},"ternary":{"aaxis":{"gridcolor":"#DFE8F3","linecolor":"#A2B1C6","ticks":""},"baxis":{"gridcolor":"#DFE8F3","linecolor":"#A2B1C6","ticks":""},"bgcolor":"white","caxis":{"gridcolor":"#DFE8F3","linecolor":"#A2B1C6","ticks":""}},"title":{"x":0.05},"xaxis":{"automargin":true,"gridcolor":"#EBF0F8","linecolor":"#EBF0F8","ticks":"","title":{"standoff":15},"zerolinecolor":"#EBF0F8","zerolinewidth":2},"yaxis":{"automargin":true,"gridcolor":"#EBF0F8","linecolor":"#EBF0F8","ticks":"","title":{"standoff":15},"zerolinecolor":"#EBF0F8","zerolinewidth":2}}},"xaxis":{"anchor":"y","domain":[0.0,0.45],"title":{"text":"E"}},"yaxis":{"anchor":"x","domain":[0.625,1.0],"title":{"text":"bound"}},"xaxis2":{"anchor":"y2","domain":[0.55,1.0],"title":{"text":"1\u002fS"}},"yaxis2":{"anchor":"x2","domain":[0.625,1.0],"title":{"text":"bound"}},"xaxis3":{"anchor":"y3","domain":[0.0,0.45],"title":{"text":"q_max"}},"yaxis3":{"anchor":"x3","domain":[0.0,0.375],"title":{"text":"penalty"}},"xaxis4":{"anchor":"y4","domain":[0.55,1.0],"title":{"text":"1 \u002f (1 - ρ²)"}},"yaxis4":{"anchor":"x4","domain":[0.0,0.375],"title":{"text":"steady-state error"}},"annotations":[{"font":{"size":16},"showarrow":false,"text":"Displayed FedDPO bound vs local steps","x":0.225,"xanchor":"center","xref":"paper","y":1.0,"yanchor":"bottom","yref":"paper"},{"font":{"size":16},"showarrow":false,"text":"Displayed FedDPO bound vs participation","x":0.775,"xanchor":"center","xref":"paper","y":1.0,"yanchor":"bottom","yref":"paper"},{"font":{"size":16},"showarrow":false,"text":"Staleness term is linear by definition","x":0.225,"xanchor":"center","xref":"paper","y":0.375,"yanchor":"bottom","yref":"paper"},{"font":{"size":16},"showarrow":false,"text":"Consensus recursion vs spectral gap","x":0.775,"xanchor":"center","xref":"paper","y":0.375,"yanchor":"bottom","yref":"paper"}],"title":{"text":"Distributed DPO theory: statement-level numerical audit"},"height":800,"width":1200}, {"responsive": true} ) }; </script> </div>
539
+ </body>
540
+ </html>
541
+ ````
542
+
543
+ ````raw
544
+ {
545
+ "scope": "numerical/consistency audit, not a proof",
546
+ "constants": {
547
+ "delta": 1.0,
548
+ "eta": 0.01,
549
+ "rounds": 1000,
550
+ "n_clients": 5,
551
+ "smoothness": 1.0,
552
+ "variance": 0.4,
553
+ "heterogeneity": 0.2
554
+ },
555
+ "partial_bound_rows": [
556
+ {
557
+ "E": 1,
558
+ "S": 1,
559
+ "optimization": 0.2,
560
+ "variance_1_over_s": 0.032,
561
+ "heterogeneity_drift": 0.00032,
562
+ "local_variance": 0.00064,
563
+ "total": 0.23296
564
+ },
565
+ {
566
+ "E": 1,
567
+ "S": 3,
568
+ "optimization": 0.2,
569
+ "variance_1_over_s": 0.010666666666666666,
570
+ "heterogeneity_drift": 0.00032,
571
+ "local_variance": 0.00021333333333333336,
572
+ "total": 0.2112
573
+ },
574
+ {
575
+ "E": 1,
576
+ "S": 5,
577
+ "optimization": 0.2,
578
+ "variance_1_over_s": 0.0064,
579
+ "heterogeneity_drift": 0.00032,
580
+ "local_variance": 0.00012800000000000002,
581
+ "total": 0.206848
582
+ },
583
+ {
584
+ "E": 3,
585
+ "S": 1,
586
+ "optimization": 0.06666666666666667,
587
+ "variance_1_over_s": 0.032,
588
+ "heterogeneity_drift": 0.0009600000000000001,
589
+ "local_variance": 0.005760000000000001,
590
+ "total": 0.10538666666666667
591
+ },
592
+ {
593
+ "E": 3,
594
+ "S": 3,
595
+ "optimization": 0.06666666666666667,
596
+ "variance_1_over_s": 0.010666666666666666,
597
+ "heterogeneity_drift": 0.0009600000000000001,
598
+ "local_variance": 0.0019200000000000005,
599
+ "total": 0.08021333333333333
600
+ },
601
+ {
602
+ "E": 3,
603
+ "S": 5,
604
+ "optimization": 0.06666666666666667,
605
+ "variance_1_over_s": 0.0064,
606
+ "heterogeneity_drift": 0.0009600000000000001,
607
+ "local_variance": 0.0011520000000000002,
608
+ "total": 0.07517866666666667
609
+ },
610
+ {
611
+ "E": 6,
612
+ "S": 1,
613
+ "optimization": 0.03333333333333333,
614
+ "variance_1_over_s": 0.032,
615
+ "heterogeneity_drift": 0.0019200000000000003,
616
+ "local_variance": 0.023040000000000005,
617
+ "total": 0.09029333333333334
618
+ },
619
+ {
620
+ "E": 6,
621
+ "S": 3,
622
+ "optimization": 0.03333333333333333,
623
+ "variance_1_over_s": 0.010666666666666666,
624
+ "heterogeneity_drift": 0.0019200000000000003,
625
+ "local_variance": 0.007680000000000002,
626
+ "total": 0.0536
627
+ },
628
+ {
629
+ "E": 6,
630
+ "S": 5,
631
+ "optimization": 0.03333333333333333,
632
+ "variance_1_over_s": 0.0064,
633
+ "heterogeneity_drift": 0.0019200000000000003,
634
+ "local_variance": 0.004608000000000001,
635
+ "total": 0.046261333333333335
636
+ },
637
+ {
638
+ "E": 12,
639
+ "S": 1,
640
+ "optimization": 0.016666666666666666,
641
+ "variance_1_over_s": 0.032,
642
+ "heterogeneity_drift": 0.0038400000000000005,
643
+ "local_variance": 0.09216000000000002,
644
+ "total": 0.1446666666666667
645
+ },
646
+ {
647
+ "E": 12,
648
+ "S": 3,
649
+ "optimization": 0.016666666666666666,
650
+ "variance_1_over_s": 0.010666666666666666,
651
+ "heterogeneity_drift": 0.0038400000000000005,
652
+ "local_variance": 0.030720000000000008,
653
+ "total": 0.06189333333333334
654
+ },
655
+ {
656
+ "E": 12,
657
+ "S": 5,
658
+ "optimization": 0.016666666666666666,
659
+ "variance_1_over_s": 0.0064,
660
+ "heterogeneity_drift": 0.0038400000000000005,
661
+ "local_variance": 0.018432000000000004,
662
+ "total": 0.04533866666666667
663
+ }
664
+ ],
665
+ "staleness_rows": [
666
+ {
667
+ "q_max": 0,
668
+ "C_q": 0.00018000000000000004,
669
+ "eta_C_q_q_max": 0.0
670
+ },
671
+ {
672
+ "q_max": 1,
673
+ "C_q": 0.00018000000000000004,
674
+ "eta_C_q_q_max": 1.8000000000000003e-06
675
+ },
676
+ {
677
+ "q_max": 2,
678
+ "C_q": 0.00018000000000000004,
679
+ "eta_C_q_q_max": 3.6000000000000007e-06
680
+ },
681
+ {
682
+ "q_max": 5,
683
+ "C_q": 0.00018000000000000004,
684
+ "eta_C_q_q_max": 9.000000000000002e-06
685
+ },
686
+ {
687
+ "q_max": 10,
688
+ "C_q": 0.00018000000000000004,
689
+ "eta_C_q_q_max": 1.8000000000000004e-05
690
+ }
691
+ ],
692
+ "lower_bound_rows": [
693
+ {
694
+ "E_budget": 1,
695
+ "claimed_scale_E_kappa_sq_over_S": 0.06666666666666667,
696
+ "one_step_algorithm_updates_used": 1,
697
+ "one_step_algorithm_is_admissible": true
698
+ },
699
+ {
700
+ "E_budget": 2,
701
+ "claimed_scale_E_kappa_sq_over_S": 0.13333333333333333,
702
+ "one_step_algorithm_updates_used": 1,
703
+ "one_step_algorithm_is_admissible": true
704
+ },
705
+ {
706
+ "E_budget": 4,
707
+ "claimed_scale_E_kappa_sq_over_S": 0.26666666666666666,
708
+ "one_step_algorithm_updates_used": 1,
709
+ "one_step_algorithm_is_admissible": true
710
+ },
711
+ {
712
+ "E_budget": 8,
713
+ "claimed_scale_E_kappa_sq_over_S": 0.5333333333333333,
714
+ "one_step_algorithm_updates_used": 1,
715
+ "one_step_algorithm_is_admissible": true
716
+ },
717
+ {
718
+ "E_budget": 16,
719
+ "claimed_scale_E_kappa_sq_over_S": 1.0666666666666667,
720
+ "one_step_algorithm_updates_used": 1,
721
+ "one_step_algorithm_is_admissible": true
722
+ }
723
+ ],
724
+ "spectral_rows": [
725
+ {
726
+ "topology": "path",
727
+ "rho": 0.872677996249965,
728
+ "one_minus_rho_sq": 0.23843311486114604,
729
+ "inverse_gap": 4.194048299802485,
730
+ "recursion_limit_numeric": 0.0002516428979856393,
731
+ "recursion_limit_closed_form": 0.00025164289798814906,
732
+ "relative_error": 9.973519694151137e-12,
733
+ "row_sum_max_error": 0.0,
734
+ "symmetry_max_error": 0.0
735
+ },
736
+ {
737
+ "topology": "ring",
738
+ "rho": 0.5393446629166316,
739
+ "one_minus_rho_sq": 0.709107334583345,
740
+ "inverse_gap": 1.4102237436136194,
741
+ "recursion_limit_numeric": 8.461342461668239e-05,
742
+ "recursion_limit_closed_form": 8.461342461681716e-05,
743
+ "relative_error": 1.5927298847701173e-12,
744
+ "row_sum_max_error": 0.0,
745
+ "symmetry_max_error": 0.0
746
+ },
747
+ {
748
+ "topology": "star",
749
+ "rho": 0.8,
750
+ "one_minus_rho_sq": 0.3599999999999999,
751
+ "inverse_gap": 2.7777777777777786,
752
+ "recursion_limit_numeric": 0.00016666666666517355,
753
+ "recursion_limit_closed_form": 0.00016666666666666672,
754
+ "relative_error": 8.958979391682217e-12,
755
+ "row_sum_max_error": 0.0,
756
+ "symmetry_max_error": 0.0
757
+ },
758
+ {
759
+ "topology": "complete",
760
+ "rho": 8.284195946896153e-17,
761
+ "one_minus_rho_sq": 1.0,
762
+ "inverse_gap": 1.0,
763
+ "recursion_limit_numeric": 6e-05,
764
+ "recursion_limit_closed_form": 6e-05,
765
+ "relative_error": 0.0,
766
+ "row_sum_max_error": 0.0,
767
+ "symmetry_max_error": 0.0
768
+ },
769
+ {
770
+ "topology": "disconnected",
771
+ "rho": 1.0,
772
+ "one_minus_rho_sq": 0.0,
773
+ "inverse_gap": Infinity,
774
+ "recursion_limit_numeric": 0.06,
775
+ "recursion_limit_closed_form": Infinity,
776
+ "relative_error": NaN,
777
+ "row_sum_max_error": 0.0,
778
+ "symmetry_max_error": 0.0
779
+ }
780
+ ],
781
+ "proof_obligations": {
782
+ "claim_1_second_moment_counterexample": {
783
+ "description": "The commented proof claims ||sum over S identical gradients||^2 <= S times the population second moment.",
784
+ "S": 3,
785
+ "identical_gradient_norm": 1.0,
786
+ "claimed_lhs": 9.0,
787
+ "claimed_rhs": 3.0,
788
+ "inequality_holds": false
789
+ },
790
+ "claim_1_constant_reshaping_counterexample": {
791
+ "description": "The commented proof attempts to absorb a non-vanishing heterogeneity term into O(eta^2 E kappa^2) by choosing eta small.",
792
+ "eta_values": [
793
+ 0.1,
794
+ 0.01,
795
+ 0.001,
796
+ 0.0001
797
+ ],
798
+ "fixed_source_term_2_kappa_sq": 0.4,
799
+ "target_terms_16_eta_sq_E_kappa_sq": [
800
+ 0.09600000000000003,
801
+ 0.0009600000000000001,
802
+ 9.600000000000001e-06,
803
+ 9.600000000000001e-08
804
+ ],
805
+ "absorption_improves_as_eta_shrinks": false
806
+ },
807
+ "claim_2_literal_variance_vanishes": {
808
+ "partial_bound_at_S_equals_N": {
809
+ "optimization": 0.06666666666666667,
810
+ "variance_1_over_s": 0.0064,
811
+ "heterogeneity_drift": 0.0009600000000000001,
812
+ "local_variance": 0.0011520000000000002,
813
+ "total": 0.07517866666666667
814
+ },
815
+ "tightened_full_bound": {
816
+ "optimization": 0.06666666666666667,
817
+ "averaged_stochastic_variance_1_over_n": 0.0016,
818
+ "heterogeneity_drift": 0.00048000000000000007,
819
+ "total": 0.06874666666666666
820
+ },
821
+ "full_bound_has_nonzero_1_over_N_stochastic_variance": true
822
+ },
823
+ "claim_4_budget_class_nesting": {
824
+ "description": "An algorithm that always uses one update is admissible for every 'at most E' budget. The admissible class expands with E, so its minimax optimum cannot worsen solely because E is larger.",
825
+ "claimed_scale_is_increasing": true,
826
+ "admissible_class_is_nested": true
827
+ },
828
+ "claim_5_recursion_double_precision": {
829
+ "max_relative_error_connected": 9.973519694151137e-12,
830
+ "all_connected_checks_below_1e-10": true,
831
+ "disconnected_control_has_zero_gap": true
832
+ }
833
+ }
834
+ }
835
+ ````
pages/claim-2-full-participation-corollary/page.md ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Claim 2: Full participation corollary
2
+
3
+
4
+ ---
5
+ <!-- trackio-cell
6
+ {"type": "markdown", "id": "cell_85aa020bf060", "created_at": "2026-07-29T13:20:42+00:00", "title": "Test plan"}
7
+ -->
8
+ This page checks the literal Claim 2 wording against Corollary 5.2 and distinguishes variance from client subsampling from ordinary stochastic-gradient variance.
9
+
10
+
11
+ ---
12
+ <!-- trackio-cell
13
+ {"type": "markdown", "id": "cell_14530d6f43de", "created_at": "2026-07-29T13:20:43+00:00", "title": "Finding"}
14
+ -->
15
+ **Verdict: contradicted as literally worded; a narrower interpretation is supported.** Corollary 5.2 states
16
+
17
+ `avg_r E||∇L(θ_r)||² ≤ 2Δ/(ηER) + 2Lηζ²_g/N + 8L²η²Eκ²`.
18
+
19
+ Thus the partial-participation terms `8Lηζ²_g/S` and `16L²η²E²ζ²_g/S` do not survive in their Theorem 5.1 form, so the extra variance from sampling clients disappears. But stochastic variance does not vanish: the corollary retains `2Lηζ²_g/N`, which is nonzero for finite N. In the audit constants `L=1`, `η=0.01`, `ζ²_g=0.4`, `N=5`, this retained term is `0.0016`. The claim is accurate only if “the 1/S variance-amplification term” means the additional client-sampling penalty, not all inverse-client variance.
20
+
21
+ Source: [paper](https://arxiv.org/abs/2605.20696) and [official code at commit 97ef67e](https://github.com/starkjiang/distributed_dpo/tree/97ef67ef9e44df8b75aeef55e4ecaa6141d6ade7).
pages/claim-3-staleness-penalty/page.md ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Claim 3: Staleness penalty
2
+
3
+
4
+ ---
5
+ <!-- trackio-cell
6
+ {"type": "markdown", "id": "cell_594b4addacaa", "created_at": "2026-07-29T13:20:45+00:00", "title": "Test plan"}
7
+ -->
8
+ This page checks the Theorem 5.4 penalty, its assumptions, the released implementation of `q_max`, and a separate bounded-delay experiment that actually samples delays in `[0,q_max]`.
9
+
10
+
11
+ ---
12
+ <!-- trackio-cell
13
+ {"type": "markdown", "id": "cell_0fdfaf3f9193", "created_at": "2026-07-29T13:20:47+00:00", "title": "Finding"}
14
+ -->
15
+ **Verdict: the stated term is present and the mechanism is numerically supported, but the theorem is not verified.** Theorem 5.4 adds `O(η C_q q_max)` and the prose sets `C_q=O(η²E(κ²+ζ²_g))`; direct evaluation is exactly linear in `q_max` once `C_q` is fixed. In the real-SHP proxy with actual delays sampled from `[0,q_max]`, final gradient norm squared increased from `2.9570157e-4` at `q=0` to `2.9586910e-4` at `q=2` and `2.9603010e-4` at `q=5`; the linear fit has positive slope `6.48e-8` per delay round and `R²=0.984`.
16
+
17
+ The proof is commented out. Assumption 5.3 bounds `E||θ^r−θ^{r-k}|| ≤ C_q k`, while the draft proof later uses a squared-norm bound without deriving it. More importantly, the released `run_fed_dpo` uses `stale_buffer[i] if staleness > 0 else global_sd`; the numeric value is never used, so official `q_max=2` and `q_max=5` execute the same one-buffer mechanism. Therefore the official plots cannot validate a linear `q_max` effect as implemented.
18
+
19
+ Resources: [SHP](https://huggingface.co/datasets/stanfordnlp/SHP), [DistilGPT-2](https://huggingface.co/distilbert/distilgpt2), and [audited algorithms.py](https://github.com/starkjiang/distributed_dpo/blob/97ef67ef9e44df8b75aeef55e4ecaa6141d6ade7/distributed_dpo/algorithms.py).
pages/claim-4-feddpo-lower-bound/page.md ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Claim 4: FedDPO lower bound
2
+
3
+
4
+ ---
5
+ <!-- trackio-cell
6
+ {"type": "markdown", "id": "cell_ea4df74361fa", "created_at": "2026-07-29T13:20:49+00:00", "title": "Test plan"}
7
+ -->
8
+ This page checks whether Theorem 5.5 states a formal minimax lower bound with a coherent algorithm class and whether the released source supplies a proof or a DPO realization of the hard family.
9
+
10
+
11
+ ---
12
+ <!-- trackio-cell
13
+ {"type": "markdown", "id": "cell_713a73667008", "created_at": "2026-07-29T13:20:50+00:00", "title": "Finding"}
14
+ -->
15
+ **Verdict: unsupported and likely false as stated.** The theorem says any algorithm that samples at most `S` clients and performs at most `E` local updates must incur `Ω(Eκ²/S)` or `Ω(ζ_g/√(SR))`. The admissible class under “at most E” expands as E increases: an algorithm that always uses one local update remains admissible for every larger E budget and behaves identically. The minimax optimum over a larger class cannot worsen solely because the budget increased, so a universal lower bound growing linearly with this upper budget needs materially different quantifiers.
16
+
17
+ No active proof is included. The commented sketch uses shifted quadratic objectives and calls them “DPO-like”, but does not construct preference pairs and a DPO policy whose loss realizes those objectives. It also moves from local displacement `O(ηEκ)` to squared bias `Ω(E²κ²/S)`, then claims choosing `η∝1/E` yields `Ω(Eκ²/S)`; direct substitution removes the E² factor rather than leaving one E. These issues prevent verification of Claim 4.
18
+
19
+ Source: [paper](https://arxiv.org/abs/2605.20696) and [source-linked official repository](https://github.com/starkjiang/distributed_dpo/tree/97ef67ef9e44df8b75aeef55e4ecaa6141d6ade7).
pages/claim-5-decdpo-spectral-rate/page.md ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Claim 5: DecDPO spectral rate
2
+
3
+
4
+ ---
5
+ <!-- trackio-cell
6
+ {"type": "markdown", "id": "cell_30089a9dbcbf", "created_at": "2026-07-29T13:20:52+00:00", "title": "Test plan"}
7
+ -->
8
+ This page checks the algebra from the displayed Theorem 6.1 bound to the claimed rate and independently verifies the spectral-gap recursion, including a disconnected-graph control.
9
+
10
+
11
+ ---
12
+ <!-- trackio-cell
13
+ {"type": "markdown", "id": "cell_79e71a709ca5", "created_at": "2026-07-29T13:20:54+00:00", "title": "Finding"}
14
+ -->
15
+ **Verdict: displayed statement and rate algebra supported; theorem proof not verified.** Theorem 6.1 displays
16
+
17
+ `avg_r E||∇L(θ̄_r)||² ≤ 2Δ/(ηR) + 32L²η²ζ²_g/(1−ρ²) + 16L²η²κ²/(1−ρ²)`
18
+
19
+ with `η≤√(1−ρ²)/(4L)`. Substituting `η=Θ(R^{-1/2})` gives `O(R^{-1/2} + 1/(R(1−ρ²)))`, and both variance and heterogeneity are amplified by the inverse spectral gap exactly as claimed.
20
+
21
+ For the scalar consensus recursion `e_{r+1}=ρ²e_r+η²v`, all four connected N=5 Metropolis graphs converged to the closed form `η²v/(1−ρ²)` with maximum relative error `9.97e-12`. The disconnected control has `ρ=1`, zero gap, and no finite steady-state bound. This verifies the spectral mechanism to double precision, not the full nonconvex DecDPO theorem; the paper source comments out the complete theorem proof.
22
+
23
+ Resources: [paper](https://arxiv.org/abs/2605.20696) and [official topology implementation](https://github.com/starkjiang/distributed_dpo/blob/97ef67ef9e44df8b75aeef55e4ecaa6141d6ade7/distributed_dpo/algorithms.py).
24
+
25
+
26
+ ---
27
+ <!-- trackio-cell
28
+ {"type": "figure", "id": "cell_76d3edbeea9b", "created_at": "2026-07-29T13:22:53+00:00", "title": "Spectral recursion audit"}
29
+ -->
30
+ ````html
31
+ <html>
32
+ <head><meta charset="utf-8" /></head>
33
+ <body>
34
+ <div style="height:800px; width:1200px;"> <script>window.PlotlyConfig = {MathJaxConfig: 'local'};</script>
35
+ <script charset="utf-8" src="https://cdn.plot.ly/plotly-3.7.0.min.js" integrity="sha256-jvTGqxNp8AGWEcvNLVuKr+8j5dGe9Yw51LQkmDH+IYA=" crossorigin="anonymous"></script> <div id="2cfc58e2-9925-4649-9835-b357bd7e6f2e" class="plotly-graph-div" style="height:100%; width:100%;"></div> <script> window.PLOTLYENV=window.PLOTLYENV || {}; if (document.getElementById("2cfc58e2-9925-4649-9835-b357bd7e6f2e")) { Plotly.newPlot( "2cfc58e2-9925-4649-9835-b357bd7e6f2e", [{"mode":"lines+markers","name":"S=1","x":[1,3,6,12],"y":[0.23296,0.10538666666666667,0.09029333333333334,0.1446666666666667],"type":"scatter","xaxis":"x","yaxis":"y"},{"mode":"lines+markers","name":"S=3","x":[1,3,6,12],"y":[0.2112,0.08021333333333333,0.0536,0.06189333333333334],"type":"scatter","xaxis":"x","yaxis":"y"},{"mode":"lines+markers","name":"S=5","x":[1,3,6,12],"y":[0.206848,0.07517866666666667,0.046261333333333335,0.04533866666666667],"type":"scatter","xaxis":"x","yaxis":"y"},{"mode":"lines+markers","name":"E=1","x":[1.0,0.3333333333333333,0.2],"y":[0.23296,0.2112,0.206848],"type":"scatter","xaxis":"x2","yaxis":"y2"},{"mode":"lines+markers","name":"E=3","x":[1.0,0.3333333333333333,0.2],"y":[0.10538666666666667,0.08021333333333333,0.07517866666666667],"type":"scatter","xaxis":"x2","yaxis":"y2"},{"mode":"lines+markers","name":"E=6","x":[1.0,0.3333333333333333,0.2],"y":[0.09029333333333334,0.0536,0.046261333333333335],"type":"scatter","xaxis":"x2","yaxis":"y2"},{"mode":"lines+markers","name":"E=12","x":[1.0,0.3333333333333333,0.2],"y":[0.1446666666666667,0.06189333333333334,0.04533866666666667],"type":"scatter","xaxis":"x2","yaxis":"y2"},{"mode":"lines+markers","name":"η Cq qmax","x":[0,1,2,5,10],"y":[0.0,1.8000000000000003e-6,3.6000000000000007e-6,9.000000000000002e-6,0.000018000000000000004],"type":"scatter","xaxis":"x3","yaxis":"y3"},{"mode":"markers+text","name":"numeric fixed point","text":["path","ring","star","complete"],"textposition":"top center","x":[4.194048299802485,1.4102237436136194,2.7777777777777786,1.0],"y":[0.0002516428979856393,0.00008461342461668239,0.00016666666666517355,0.00006],"type":"scatter","xaxis":"x4","yaxis":"y4"}], {"template":{"data":{"barpolar":[{"marker":{"line":{"color":"white","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"barpolar"}],"bar":[{"error_x":{"color":"#2a3f5f"},"error_y":{"color":"#2a3f5f"},"marker":{"line":{"color":"white","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"bar"}],"carpet":[{"aaxis":{"endlinecolor":"#2a3f5f","gridcolor":"#C8D4E3","linecolor":"#C8D4E3","minorgridcolor":"#C8D4E3","startlinecolor":"#2a3f5f"},"baxis":{"endlinecolor":"#2a3f5f","gridcolor":"#C8D4E3","linecolor":"#C8D4E3","minorgridcolor":"#C8D4E3","startlinecolor":"#2a3f5f"},"type":"carpet"}],"choropleth":[{"colorbar":{"outlinewidth":0,"ticks":""},"type":"choropleth"}],"contourcarpet":[{"colorbar":{"outlinewidth":0,"ticks":""},"type":"contourcarpet"}],"contour":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"contour"}],"heatmap":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"heatmap"}],"histogram2dcontour":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"histogram2dcontour"}],"histogram2d":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"histogram2d"}],"histogram":[{"marker":{"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"histogram"}],"mesh3d":[{"colorbar":{"outlinewidth":0,"ticks":""},"type":"mesh3d"}],"parcoords":[{"line":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"parcoords"}],"pie":[{"automargin":true,"type":"pie"}],"scatter3d":[{"line":{"colorbar":{"outlinewidth":0,"ticks":""}},"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatter3d"}],"scattercarpet":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattercarpet"}],"scattergeo":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattergeo"}],"scattergl":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattergl"}],"scattermapbox":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattermapbox"}],"scattermap":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattermap"}],"scatterpolargl":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatterpolargl"}],"scatterpolar":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatterpolar"}],"scatter":[{"fillpattern":{"fillmode":"overlay","size":10,"solidity":0.2},"type":"scatter"}],"scatterternary":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatterternary"}],"surface":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"surface"}],"table":[{"cells":{"fill":{"color":"#EBF0F8"},"line":{"color":"white"}},"header":{"fill":{"color":"#C8D4E3"},"line":{"color":"white"}},"type":"table"}]},"layout":{"annotationdefaults":{"arrowcolor":"#2a3f5f","arrowhead":0,"arrowwidth":1},"autotypenumbers":"strict","coloraxis":{"colorbar":{"outlinewidth":0,"ticks":""}},"colorscale":{"diverging":[[0,"#8e0152"],[0.1,"#c51b7d"],[0.2,"#de77ae"],[0.3,"#f1b6da"],[0.4,"#fde0ef"],[0.5,"#f7f7f7"],[0.6,"#e6f5d0"],[0.7,"#b8e186"],[0.8,"#7fbc41"],[0.9,"#4d9221"],[1,"#276419"]],"sequential":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"sequentialminus":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]},"colorway":["#636efa","#EF553B","#00cc96","#ab63fa","#FFA15A","#19d3f3","#FF6692","#B6E880","#FF97FF","#FECB52"],"font":{"color":"#2a3f5f"},"geo":{"bgcolor":"white","lakecolor":"white","landcolor":"white","showlakes":true,"showland":true,"subunitcolor":"#C8D4E3"},"hoverlabel":{"align":"left"},"hovermode":"closest","mapbox":{"style":"light"},"paper_bgcolor":"white","plot_bgcolor":"white","polar":{"angularaxis":{"gridcolor":"#EBF0F8","linecolor":"#EBF0F8","ticks":""},"bgcolor":"white","radialaxis":{"gridcolor":"#EBF0F8","linecolor":"#EBF0F8","ticks":""}},"scene":{"xaxis":{"backgroundcolor":"white","gridcolor":"#DFE8F3","gridwidth":2,"linecolor":"#EBF0F8","showbackground":true,"ticks":"","zerolinecolor":"#EBF0F8"},"yaxis":{"backgroundcolor":"white","gridcolor":"#DFE8F3","gridwidth":2,"linecolor":"#EBF0F8","showbackground":true,"ticks":"","zerolinecolor":"#EBF0F8"},"zaxis":{"backgroundcolor":"white","gridcolor":"#DFE8F3","gridwidth":2,"linecolor":"#EBF0F8","showbackground":true,"ticks":"","zerolinecolor":"#EBF0F8"}},"shapedefaults":{"line":{"color":"#2a3f5f"}},"ternary":{"aaxis":{"gridcolor":"#DFE8F3","linecolor":"#A2B1C6","ticks":""},"baxis":{"gridcolor":"#DFE8F3","linecolor":"#A2B1C6","ticks":""},"bgcolor":"white","caxis":{"gridcolor":"#DFE8F3","linecolor":"#A2B1C6","ticks":""}},"title":{"x":0.05},"xaxis":{"automargin":true,"gridcolor":"#EBF0F8","linecolor":"#EBF0F8","ticks":"","title":{"standoff":15},"zerolinecolor":"#EBF0F8","zerolinewidth":2},"yaxis":{"automargin":true,"gridcolor":"#EBF0F8","linecolor":"#EBF0F8","ticks":"","title":{"standoff":15},"zerolinecolor":"#EBF0F8","zerolinewidth":2}}},"xaxis":{"anchor":"y","domain":[0.0,0.45],"title":{"text":"E"}},"yaxis":{"anchor":"x","domain":[0.625,1.0],"title":{"text":"bound"}},"xaxis2":{"anchor":"y2","domain":[0.55,1.0],"title":{"text":"1\u002fS"}},"yaxis2":{"anchor":"x2","domain":[0.625,1.0],"title":{"text":"bound"}},"xaxis3":{"anchor":"y3","domain":[0.0,0.45],"title":{"text":"q_max"}},"yaxis3":{"anchor":"x3","domain":[0.0,0.375],"title":{"text":"penalty"}},"xaxis4":{"anchor":"y4","domain":[0.55,1.0],"title":{"text":"1 \u002f (1 - ρ²)"}},"yaxis4":{"anchor":"x4","domain":[0.0,0.375],"title":{"text":"steady-state error"}},"annotations":[{"font":{"size":16},"showarrow":false,"text":"Displayed FedDPO bound vs local steps","x":0.225,"xanchor":"center","xref":"paper","y":1.0,"yanchor":"bottom","yref":"paper"},{"font":{"size":16},"showarrow":false,"text":"Displayed FedDPO bound vs participation","x":0.775,"xanchor":"center","xref":"paper","y":1.0,"yanchor":"bottom","yref":"paper"},{"font":{"size":16},"showarrow":false,"text":"Staleness term is linear by definition","x":0.225,"xanchor":"center","xref":"paper","y":0.375,"yanchor":"bottom","yref":"paper"},{"font":{"size":16},"showarrow":false,"text":"Consensus recursion vs spectral gap","x":0.775,"xanchor":"center","xref":"paper","y":0.375,"yanchor":"bottom","yref":"paper"}],"title":{"text":"Distributed DPO theory: statement-level numerical audit"},"height":800,"width":1200}, {"responsive": true} ) }; </script> </div>
36
+ </body>
37
+ </html>
38
+ ````
39
+
40
+ ````raw
41
+ topology,rho,one_minus_rho_sq,inverse_gap,recursion_limit_numeric,recursion_limit_closed_form,relative_error,row_sum_max_error,symmetry_max_error
42
+ path,0.872677996249965,0.23843311486114604,4.194048299802485,0.0002516428979856393,0.00025164289798814906,9.973519694151137e-12,0.0,0.0
43
+ ring,0.5393446629166316,0.709107334583345,1.4102237436136194,8.461342461668239e-05,8.461342461681716e-05,1.5927298847701173e-12,0.0,0.0
44
+ star,0.8,0.3599999999999999,2.7777777777777786,0.00016666666666517355,0.00016666666666666672,8.958979391682217e-12,0.0,0.0
45
+ complete,8.284195946896153e-17,1.0,1.0,6e-05,6e-05,0.0,0.0,0.0
46
+ disconnected,1.0,0.0,inf,0.06,inf,nan,0.0,0.0
47
+
48
+ ````
pages/claim-6-shp-numerical-results/page.md ADDED
@@ -0,0 +1,2080 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Claim 6: SHP numerical results
2
+
3
+
4
+ ---
5
+ <!-- trackio-cell
6
+ {"type": "markdown", "id": "cell_c4f1bdc9153c", "created_at": "2026-07-29T13:21:24+00:00", "title": "Test plan"}
7
+ -->
8
+ This page attempts the paper Section 7 ablations with real SHP data and N=5. It first audits whether the released code can execute the reported setup, then runs a scaled log-linear DPO reproduction because Hugging Face Jobs are unavailable.
9
+
10
+
11
+ ---
12
+ <!-- trackio-cell
13
+ {"type": "code", "id": "cell_d65a71cc1cf2", "created_at": "2026-07-29T13:17:01+00:00", "title": "Run: uv empirical_repro.py (exit 0)", "command": ["uv", "run", "empirical_repro.py", "--output", "empirical_outputs", "--rounds", "80", "--seeds", "5", "--dimension", "1024", "--rows-per-domain", "160", "--train-rows", "120"], "exit_code": 0, "duration_s": 58.641}
14
+ -->
15
+ ````bash
16
+ $ uv run empirical_repro.py --output empirical_outputs --rounds 80 --seeds 5 --dimension 1024 --rows-per-domain 160 --train-rows 120
17
+ ````
18
+
19
+ exit 0 · 58.6s
20
+
21
+
22
+ ````python title=empirical_repro.py
23
+ # /// script
24
+ # requires-python = ">=3.11"
25
+ # dependencies = [
26
+ # "duckdb>=1.3",
27
+ # "numpy>=2.0",
28
+ # "plotly>=6.0",
29
+ # "trackio>=0.33.0",
30
+ # ]
31
+ # ///
32
+ """Scaled SHP reproduction using a log-linear DPO policy.
33
+
34
+ The paper's GPU experiment uses DistilGPT-2. Because the Hub Jobs canary is
35
+ blocked by account credit (HTTP 402), this local fallback uses the same N=5
36
+ client structure and real SHP preference pairs but a 1024-dimensional hashed
37
+ log-linear policy. This directly matches the policy class of the theory and
38
+ is intentionally labelled a scaled proxy, not a full LLM replication.
39
+ """
40
+
41
+ from __future__ import annotations
42
+
43
+ import argparse
44
+ import csv
45
+ import hashlib
46
+ import json
47
+ import math
48
+ import random
49
+ import re
50
+ import time
51
+ from collections import defaultdict
52
+ from pathlib import Path
53
+
54
+ import duckdb
55
+ import numpy as np
56
+ import plotly.graph_objects as go
57
+ from plotly.subplots import make_subplots
58
+ import trackio
59
+
60
+
61
+ PARQUET_URLS = [
62
+ "https://huggingface.co/datasets/stanfordnlp/SHP/resolve/"
63
+ "refs%2Fconvert%2Fparquet/default/train/0000.parquet",
64
+ "https://huggingface.co/datasets/stanfordnlp/SHP/resolve/"
65
+ "refs%2Fconvert%2Fparquet/default/train/0001.parquet",
66
+ ]
67
+ DOMAINS = [
68
+ "askacademia_train",
69
+ "askbaking_train",
70
+ "askcarguys_train",
71
+ "askphilosophy_train",
72
+ "legaladvice_train",
73
+ ]
74
+ TOKEN_RE = re.compile(r"[A-Za-z][A-Za-z0-9_'-]{1,}")
75
+
76
+
77
+ def load_shp(rows_per_domain: int) -> dict[int, list[dict]]:
78
+ urls = ", ".join(f"'{url}'" for url in PARQUET_URLS)
79
+ domains = ", ".join(f"'{domain}'" for domain in DOMAINS)
80
+ query = f"""
81
+ WITH ranked AS (
82
+ SELECT
83
+ domain,
84
+ human_ref_A,
85
+ human_ref_B,
86
+ score_A,
87
+ score_B,
88
+ ROW_NUMBER() OVER (PARTITION BY domain ORDER BY post_id, c_root_id_A, c_root_id_B) AS rn
89
+ FROM read_parquet([{urls}])
90
+ WHERE domain IN ({domains}) AND score_A != score_B
91
+ )
92
+ SELECT domain, human_ref_A, human_ref_B, score_A, score_B
93
+ FROM ranked
94
+ WHERE rn <= {int(rows_per_domain)}
95
+ ORDER BY domain, rn
96
+ """
97
+ rows = duckdb.sql(query).fetchall()
98
+ grouped: dict[str, list[dict]] = defaultdict(list)
99
+ for domain, text_a, text_b, score_a, score_b in rows:
100
+ if score_a > score_b:
101
+ chosen, rejected = text_a, text_b
102
+ else:
103
+ chosen, rejected = text_b, text_a
104
+ if chosen and rejected and chosen != rejected:
105
+ grouped[domain].append(
106
+ {"chosen": chosen[-500:], "rejected": rejected[-500:]}
107
+ )
108
+ missing = [domain for domain in DOMAINS if len(grouped[domain]) < rows_per_domain]
109
+ if missing:
110
+ raise RuntimeError(f"insufficient rows for domains: {missing}")
111
+ return {index: grouped[domain] for index, domain in enumerate(DOMAINS)}
112
+
113
+
114
+ def stable_bucket(token: str, dimension: int) -> tuple[int, float]:
115
+ digest = hashlib.blake2b(token.encode("utf-8"), digest_size=8).digest()
116
+ value = int.from_bytes(digest, "little")
117
+ return value % dimension, 1.0 if (value >> 63) == 0 else -1.0
118
+
119
+
120
+ def text_vector(text: str, dimension: int) -> np.ndarray:
121
+ vector = np.zeros(dimension, dtype=np.float64)
122
+ tokens = TOKEN_RE.findall(text.lower())
123
+ for token in tokens:
124
+ index, sign = stable_bucket(token, dimension)
125
+ vector[index] += sign
126
+ norm = np.linalg.norm(vector)
127
+ if norm > 0:
128
+ vector /= norm
129
+ return vector
130
+
131
+
132
+ def featurize(
133
+ data: dict[int, list[dict]],
134
+ dimension: int,
135
+ ) -> dict[int, np.ndarray]:
136
+ result = {}
137
+ for client, rows in data.items():
138
+ result[client] = np.stack(
139
+ [
140
+ text_vector(row["chosen"], dimension)
141
+ - text_vector(row["rejected"], dimension)
142
+ for row in rows
143
+ ]
144
+ )
145
+ return result
146
+
147
+
148
+ def sigmoid_negative(z: np.ndarray) -> np.ndarray:
149
+ return np.where(
150
+ z >= 0,
151
+ np.exp(-z) / (1.0 + np.exp(-z)),
152
+ 1.0 / (1.0 + np.exp(z)),
153
+ )
154
+
155
+
156
+ def batch_gradient(
157
+ theta: np.ndarray,
158
+ features: np.ndarray,
159
+ beta: float,
160
+ ) -> np.ndarray:
161
+ z = beta * (features @ theta)
162
+ weights = -beta * sigmoid_negative(z)
163
+ return (weights[:, None] * features).mean(axis=0)
164
+
165
+
166
+ def batch_loss(theta: np.ndarray, features: np.ndarray, beta: float) -> float:
167
+ z = beta * (features @ theta)
168
+ return float(np.logaddexp(0.0, -z).mean())
169
+
170
+
171
+ def evaluate(
172
+ theta: np.ndarray,
173
+ eval_features: dict[int, np.ndarray],
174
+ beta: float,
175
+ ) -> dict[str, float]:
176
+ pooled = np.concatenate(list(eval_features.values()), axis=0)
177
+ global_gradient = batch_gradient(theta, pooled, beta)
178
+ client_gradients = np.stack(
179
+ [batch_gradient(theta, rows, beta) for rows in eval_features.values()]
180
+ )
181
+ heterogeneity = float(
182
+ np.mean(np.sum((client_gradients - global_gradient) ** 2, axis=1))
183
+ )
184
+ return {
185
+ "loss": batch_loss(theta, pooled, beta),
186
+ "grad_norm_sq": float(global_gradient @ global_gradient),
187
+ "kappa_sq_proxy": heterogeneity,
188
+ }
189
+
190
+
191
+ def local_update(
192
+ start: np.ndarray,
193
+ features: np.ndarray,
194
+ *,
195
+ steps: int,
196
+ batch_size: int,
197
+ learning_rate: float,
198
+ beta: float,
199
+ rng: np.random.Generator,
200
+ ) -> tuple[np.ndarray, float]:
201
+ theta = start.copy()
202
+ grad_norms = []
203
+ for _ in range(steps):
204
+ indexes = rng.choice(
205
+ len(features),
206
+ size=min(batch_size, len(features)),
207
+ replace=False,
208
+ )
209
+ gradient = batch_gradient(theta, features[indexes], beta)
210
+ theta -= learning_rate * gradient
211
+ grad_norms.append(float(gradient @ gradient))
212
+ return theta, float(np.mean(grad_norms))
213
+
214
+
215
+ def run_fed(
216
+ train: dict[int, np.ndarray],
217
+ evaluation: dict[int, np.ndarray],
218
+ *,
219
+ rounds: int,
220
+ local_steps: int,
221
+ participation: int,
222
+ q_max: int,
223
+ learning_rate: float,
224
+ beta: float,
225
+ batch_size: int,
226
+ seed: int,
227
+ ) -> list[dict]:
228
+ rng = np.random.default_rng(seed)
229
+ n_clients = len(train)
230
+ theta = np.zeros(train[0].shape[1], dtype=np.float64)
231
+ history = [theta.copy()]
232
+ rows = []
233
+ for round_index in range(rounds):
234
+ selected = rng.choice(n_clients, size=participation, replace=False)
235
+ local_models = []
236
+ local_grad_norms = []
237
+ delays = []
238
+ for client in selected:
239
+ delay = int(rng.integers(0, q_max + 1)) if q_max else 0
240
+ delay = min(delay, len(history) - 1)
241
+ start = history[-1 - delay]
242
+ local_model, grad_norm = local_update(
243
+ start,
244
+ train[int(client)],
245
+ steps=local_steps,
246
+ batch_size=batch_size,
247
+ learning_rate=learning_rate,
248
+ beta=beta,
249
+ rng=rng,
250
+ )
251
+ local_models.append(local_model)
252
+ local_grad_norms.append(grad_norm)
253
+ delays.append(delay)
254
+ theta = np.mean(local_models, axis=0)
255
+ history.append(theta.copy())
256
+ metrics = evaluate(theta, evaluation, beta)
257
+ rows.append(
258
+ {
259
+ "round": round_index,
260
+ **metrics,
261
+ "train_grad_norm_sq": float(np.mean(local_grad_norms)),
262
+ "mean_delay": float(np.mean(delays)),
263
+ }
264
+ )
265
+ return rows
266
+
267
+
268
+ def mixing_matrix(graph_type: str, n: int) -> tuple[np.ndarray, float, float]:
269
+ if graph_type == "path":
270
+ edges = [(i, i + 1) for i in range(n - 1)]
271
+ elif graph_type == "ring":
272
+ edges = [(i, (i + 1) % n) for i in range(n)]
273
+ elif graph_type == "star":
274
+ edges = [(0, i) for i in range(1, n)]
275
+ elif graph_type == "complete":
276
+ edges = [(i, j) for i in range(n) for j in range(i + 1, n)]
277
+ else:
278
+ raise ValueError(graph_type)
279
+ degree = np.zeros(n, dtype=int)
280
+ for i, j in edges:
281
+ degree[i] += 1
282
+ degree[j] += 1
283
+ pi = np.zeros((n, n), dtype=np.float64)
284
+ for i, j in edges:
285
+ weight = 1.0 / (1 + max(degree[i], degree[j]))
286
+ pi[i, j] = weight
287
+ pi[j, i] = weight
288
+ for i in range(n):
289
+ pi[i, i] = 1.0 - pi[i].sum()
290
+ eigenvalues = np.sort(np.abs(np.linalg.eigvalsh(pi)))[::-1]
291
+ rho = float(eigenvalues[1])
292
+ return pi, rho, 1.0 - rho**2
293
+
294
+
295
+ def run_decentralized(
296
+ train: dict[int, np.ndarray],
297
+ evaluation: dict[int, np.ndarray],
298
+ *,
299
+ rounds: int,
300
+ local_steps: int,
301
+ topology: str,
302
+ learning_rate: float,
303
+ beta: float,
304
+ batch_size: int,
305
+ seed: int,
306
+ ) -> list[dict]:
307
+ rng = np.random.default_rng(seed)
308
+ n_clients = len(train)
309
+ dimension = train[0].shape[1]
310
+ agents = np.zeros((n_clients, dimension), dtype=np.float64)
311
+ pi, rho, spectral_gap = mixing_matrix(topology, n_clients)
312
+ rows = []
313
+ for round_index in range(rounds):
314
+ local_grad_norms = []
315
+ for client in range(n_clients):
316
+ agents[client], grad_norm = local_update(
317
+ agents[client],
318
+ train[client],
319
+ steps=local_steps,
320
+ batch_size=batch_size,
321
+ learning_rate=learning_rate,
322
+ beta=beta,
323
+ rng=rng,
324
+ )
325
+ local_grad_norms.append(grad_norm)
326
+ consensus_before = float(
327
+ np.mean(np.sum((agents - agents.mean(axis=0)) ** 2, axis=1))
328
+ )
329
+ agents = pi @ agents
330
+ mean_theta = agents.mean(axis=0)
331
+ consensus_after = float(
332
+ np.mean(np.sum((agents - mean_theta) ** 2, axis=1))
333
+ )
334
+ metrics = evaluate(mean_theta, evaluation, beta)
335
+ rows.append(
336
+ {
337
+ "round": round_index,
338
+ **metrics,
339
+ "train_grad_norm_sq": float(np.mean(local_grad_norms)),
340
+ "consensus_before": consensus_before,
341
+ "consensus_after": consensus_after,
342
+ "rho": rho,
343
+ "spectral_gap": spectral_gap,
344
+ }
345
+ )
346
+ return rows
347
+
348
+
349
+ def linear_fit(x: list[float], y: list[float]) -> dict[str, float]:
350
+ coefficients = np.polyfit(np.asarray(x), np.asarray(y), 1)
351
+ predicted = np.polyval(coefficients, x)
352
+ residual = float(np.sum((np.asarray(y) - predicted) ** 2))
353
+ total = float(np.sum((np.asarray(y) - np.mean(y)) ** 2))
354
+ return {
355
+ "slope": float(coefficients[0]),
356
+ "intercept": float(coefficients[1]),
357
+ "r_squared": 1.0 - residual / total if total > 0 else 1.0,
358
+ }
359
+
360
+
361
+ def summarize(
362
+ trajectories: list[dict],
363
+ configurations: list[dict],
364
+ ) -> tuple[list[dict], dict]:
365
+ grouped: dict[tuple, list[dict]] = defaultdict(list)
366
+ for row in trajectories:
367
+ grouped[(row["study"], row["value"], row["seed"])].append(row)
368
+
369
+ final_by_config: dict[tuple, list[dict]] = defaultdict(list)
370
+ for (study, value, seed), rows in grouped.items():
371
+ final_by_config[(study, value)].append(max(rows, key=lambda row: row["round"]))
372
+
373
+ summary_rows = []
374
+ for config in configurations:
375
+ key = (config["study"], str(config["value"]))
376
+ finals = final_by_config[key]
377
+ row = {
378
+ "study": key[0],
379
+ "value": key[1],
380
+ "seeds": len(finals),
381
+ "final_grad_norm_sq_mean": float(
382
+ np.mean([item["grad_norm_sq"] for item in finals])
383
+ ),
384
+ "final_grad_norm_sq_std": float(
385
+ np.std([item["grad_norm_sq"] for item in finals])
386
+ ),
387
+ "final_loss_mean": float(np.mean([item["loss"] for item in finals])),
388
+ "final_loss_std": float(np.std([item["loss"] for item in finals])),
389
+ "final_consensus_mean": float(
390
+ np.mean([item.get("consensus_after", math.nan) for item in finals])
391
+ )
392
+ if "consensus_after" in finals[0]
393
+ else math.nan,
394
+ "spectral_gap": float(finals[0].get("spectral_gap", math.nan)),
395
+ }
396
+ summary_rows.append(row)
397
+
398
+ participation = [
399
+ row for row in summary_rows if row["study"] == "participation"
400
+ ]
401
+ staleness = [row for row in summary_rows if row["study"] == "staleness"]
402
+ topology = [row for row in summary_rows if row["study"] == "topology"]
403
+ fits = {
404
+ "participation_grad_vs_inverse_S": linear_fit(
405
+ [1.0 / float(row["value"]) for row in participation],
406
+ [row["final_grad_norm_sq_mean"] for row in participation],
407
+ ),
408
+ "staleness_grad_vs_q_max": linear_fit(
409
+ [float(row["value"]) for row in staleness],
410
+ [row["final_grad_norm_sq_mean"] for row in staleness],
411
+ ),
412
+ "topology_consensus_vs_inverse_gap": linear_fit(
413
+ [1.0 / row["spectral_gap"] for row in topology],
414
+ [row["final_consensus_mean"] for row in topology],
415
+ ),
416
+ }
417
+ return summary_rows, fits
418
+
419
+
420
+ def write_csv(path: Path, rows: list[dict]) -> None:
421
+ keys = sorted({key for row in rows for key in row})
422
+ with path.open("w", newline="", encoding="utf-8") as handle:
423
+ writer = csv.DictWriter(handle, fieldnames=keys)
424
+ writer.writeheader()
425
+ writer.writerows(rows)
426
+
427
+
428
+ def make_figure(summary_rows: list[dict], output: Path) -> None:
429
+ fig = make_subplots(
430
+ rows=2,
431
+ cols=2,
432
+ subplot_titles=(
433
+ "Local steps E",
434
+ "Participation S",
435
+ "Actual bounded staleness q_max",
436
+ "Topology and consensus",
437
+ ),
438
+ )
439
+ panels = [
440
+ ("local_steps", 1, 1),
441
+ ("participation", 1, 2),
442
+ ("staleness", 2, 1),
443
+ ]
444
+ for study, row_index, col_index in panels:
445
+ rows = [row for row in summary_rows if row["study"] == study]
446
+ x = [float(row["value"]) for row in rows]
447
+ y = [row["final_grad_norm_sq_mean"] for row in rows]
448
+ error = [row["final_grad_norm_sq_std"] for row in rows]
449
+ fig.add_trace(
450
+ go.Scatter(
451
+ x=x,
452
+ y=y,
453
+ error_y={"type": "data", "array": error},
454
+ mode="lines+markers",
455
+ name=study,
456
+ ),
457
+ row=row_index,
458
+ col=col_index,
459
+ )
460
+ topology_rows = [
461
+ row for row in summary_rows if row["study"] == "topology"
462
+ ]
463
+ fig.add_trace(
464
+ go.Scatter(
465
+ x=[1.0 / row["spectral_gap"] for row in topology_rows],
466
+ y=[row["final_consensus_mean"] for row in topology_rows],
467
+ mode="markers+text",
468
+ text=[row["value"] for row in topology_rows],
469
+ textposition="top center",
470
+ name="topology",
471
+ ),
472
+ row=2,
473
+ col=2,
474
+ )
475
+ fig.update_layout(
476
+ title="Scaled SHP log-linear DPO reproduction (N=5, mean ± std over seeds)",
477
+ template="plotly_white",
478
+ width=1200,
479
+ height=800,
480
+ )
481
+ fig.update_xaxes(title_text="E", row=1, col=1)
482
+ fig.update_xaxes(title_text="S", row=1, col=2)
483
+ fig.update_xaxes(title_text="q_max", row=2, col=1)
484
+ fig.update_xaxes(title_text="1 / (1 - ρ²)", row=2, col=2)
485
+ fig.update_yaxes(title_text="final gradient norm²", row=1, col=1)
486
+ fig.update_yaxes(title_text="final gradient norm²", row=1, col=2)
487
+ fig.update_yaxes(title_text="final gradient norm²", row=2, col=1)
488
+ fig.update_yaxes(title_text="final consensus error", row=2, col=2)
489
+ fig.write_html(output, include_plotlyjs="cdn")
490
+
491
+
492
+ def main() -> None:
493
+ parser = argparse.ArgumentParser()
494
+ parser.add_argument("--output", type=Path, default=Path("empirical_outputs"))
495
+ parser.add_argument("--rounds", type=int, default=80)
496
+ parser.add_argument("--seeds", type=int, default=5)
497
+ parser.add_argument("--dimension", type=int, default=1024)
498
+ parser.add_argument("--rows-per-domain", type=int, default=160)
499
+ parser.add_argument("--train-rows", type=int, default=120)
500
+ parser.add_argument("--learning-rate", type=float, default=0.25)
501
+ parser.add_argument("--beta", type=float, default=0.2)
502
+ parser.add_argument("--batch-size", type=int, default=16)
503
+ args = parser.parse_args()
504
+ args.output.mkdir(parents=True, exist_ok=True)
505
+ started = time.perf_counter()
506
+
507
+ raw = load_shp(args.rows_per_domain)
508
+ features = featurize(raw, args.dimension)
509
+ train = {client: values[: args.train_rows] for client, values in features.items()}
510
+ evaluation = {
511
+ client: values[args.train_rows :] for client, values in features.items()
512
+ }
513
+
514
+ configurations = [
515
+ *[
516
+ {
517
+ "study": "local_steps",
518
+ "value": e,
519
+ "kind": "fed",
520
+ "local_steps": e,
521
+ "participation": 5,
522
+ "q_max": 0,
523
+ }
524
+ for e in [1, 3, 6]
525
+ ],
526
+ *[
527
+ {
528
+ "study": "participation",
529
+ "value": s,
530
+ "kind": "fed",
531
+ "local_steps": 3,
532
+ "participation": s,
533
+ "q_max": 0,
534
+ }
535
+ for s in [1, 3, 5]
536
+ ],
537
+ *[
538
+ {
539
+ "study": "staleness",
540
+ "value": q,
541
+ "kind": "fed",
542
+ "local_steps": 3,
543
+ "participation": 3,
544
+ "q_max": q,
545
+ }
546
+ for q in [0, 2, 5]
547
+ ],
548
+ *[
549
+ {
550
+ "study": "topology",
551
+ "value": topology,
552
+ "kind": "decentralized",
553
+ "local_steps": 5,
554
+ "topology": topology,
555
+ }
556
+ for topology in ["path", "ring", "star", "complete"]
557
+ ],
558
+ ]
559
+
560
+ trajectories: list[dict] = []
561
+ for config_index, config in enumerate(configurations):
562
+ for seed in range(42, 42 + args.seeds):
563
+ if config["kind"] == "fed":
564
+ rows = run_fed(
565
+ train,
566
+ evaluation,
567
+ rounds=args.rounds,
568
+ local_steps=config["local_steps"],
569
+ participation=config["participation"],
570
+ q_max=config["q_max"],
571
+ learning_rate=args.learning_rate,
572
+ beta=args.beta,
573
+ batch_size=args.batch_size,
574
+ seed=seed,
575
+ )
576
+ else:
577
+ rows = run_decentralized(
578
+ train,
579
+ evaluation,
580
+ rounds=args.rounds,
581
+ local_steps=config["local_steps"],
582
+ topology=config["topology"],
583
+ learning_rate=args.learning_rate,
584
+ beta=args.beta,
585
+ batch_size=args.batch_size,
586
+ seed=seed,
587
+ )
588
+ for row in rows:
589
+ trajectories.append(
590
+ {
591
+ "study": config["study"],
592
+ "value": str(config["value"]),
593
+ "seed": seed,
594
+ **row,
595
+ }
596
+ )
597
+ print(
598
+ f"[{config_index + 1}/{len(configurations)}] "
599
+ f"{config['study']}={config['value']} complete"
600
+ )
601
+
602
+ summary_rows, fits = summarize(trajectories, configurations)
603
+ duration = time.perf_counter() - started
604
+ metadata = {
605
+ "scope": "scaled local proxy; real SHP, N=5, log-linear policy",
606
+ "full_paper_backbone": "distilgpt2 (~82M)",
607
+ "reproduction_policy": f"hashed log-linear ({args.dimension} dimensions)",
608
+ "dataset": "https://huggingface.co/datasets/stanfordnlp/SHP",
609
+ "dataset_revision": "e94b5f32602712d78ed494fe79105b1959396686",
610
+ "domains": DOMAINS,
611
+ "n_clients": 5,
612
+ "train_pairs_per_client": args.train_rows,
613
+ "eval_pairs_per_client": args.rows_per_domain - args.train_rows,
614
+ "rounds": args.rounds,
615
+ "seeds": list(range(42, 42 + args.seeds)),
616
+ "learning_rate": args.learning_rate,
617
+ "beta": args.beta,
618
+ "batch_size": args.batch_size,
619
+ "wall_time_seconds": duration,
620
+ "hardware": "Apple M1 CPU, 16 GB unified memory",
621
+ "hf_jobs_status": "blocked before submission: HTTP 402 insufficient credits",
622
+ "billed_cost_usd": 0.0,
623
+ }
624
+ report = {
625
+ "metadata": metadata,
626
+ "fits": fits,
627
+ "summary": summary_rows,
628
+ }
629
+
630
+ trajectories_path = args.output / "trajectories.csv"
631
+ summary_path = args.output / "summary.csv"
632
+ report_path = args.output / "results.json"
633
+ figure_path = args.output / "ablation_summary.html"
634
+ write_csv(trajectories_path, trajectories)
635
+ write_csv(summary_path, summary_rows)
636
+ report_path.write_text(json.dumps(report, indent=2), encoding="utf-8")
637
+ make_figure(summary_rows, figure_path)
638
+
639
+ trackio.init(
640
+ project="ddpo-shp-scaled-reproduction",
641
+ name="n5-loglinear-five-seeds",
642
+ config=metadata,
643
+ )
644
+ for step, row in enumerate(summary_rows):
645
+ trackio.log(
646
+ {
647
+ "summary/final_grad_norm_sq": row["final_grad_norm_sq_mean"],
648
+ "summary/final_loss": row["final_loss_mean"],
649
+ "summary/config_index": step,
650
+ },
651
+ step=step,
652
+ )
653
+ trackio.log(
654
+ {
655
+ "fits/participation_slope": fits[
656
+ "participation_grad_vs_inverse_S"
657
+ ]["slope"],
658
+ "fits/staleness_slope": fits["staleness_grad_vs_q_max"]["slope"],
659
+ "fits/topology_slope": fits[
660
+ "topology_consensus_vs_inverse_gap"
661
+ ]["slope"],
662
+ "wall_time_seconds": duration,
663
+ },
664
+ step=len(summary_rows),
665
+ )
666
+ trackio.log_artifact(
667
+ args.output,
668
+ name="ddpo-shp-scaled-results",
669
+ type="dataset",
670
+ aliases=["reproduction"],
671
+ )
672
+ trackio.finish()
673
+
674
+ print(json.dumps(report, indent=2))
675
+
676
+
677
+ if __name__ == "__main__":
678
+ main()
679
+
680
+ ````
681
+
682
+
683
+ ````output
684
+ Downloading duckdb (14.8MiB)
685
+ Downloaded duckdb
686
+ Installed 32 packages in 77ms
687
+ [1/13] local_steps=1 complete
688
+ [2/13] local_steps=3 complete
689
+ [3/13] local_steps=6 complete
690
+ [4/13] participation=1 complete
691
+ [5/13] participation=3 complete
692
+ [6/13] participation=5 complete
693
+ [7/13] staleness=0 complete
694
+ [8/13] staleness=2 complete
695
+ [9/13] staleness=5 complete
696
+ [10/13] topology=path complete
697
+ [11/13] topology=ring complete
698
+ [12/13] topology=star complete
699
+ [13/13] topology=complete complete
700
+ * Trackio project initialized: ddpo-shp-scaled-reproduction
701
+ * Trackio metrics logged to: /Users/test/.cache/huggingface/trackio
702
+ * View dashboard by running in your terminal:
703
+ trackio show --project "ddpo-shp-scaled-reproduction"
704
+ * or by running in Python: trackio.show(project="ddpo-shp-scaled-reproduction")
705
+ * Created new run: n5-loglinear-five-seeds
706
+ * Run finished. Uploading logs to Trackio (please wait...)
707
+ {
708
+ "metadata": {
709
+ "scope": "scaled local proxy; real SHP, N=5, log-linear policy",
710
+ "full_paper_backbone": "distilgpt2 (~82M)",
711
+ "reproduction_policy": "hashed log-linear (1024 dimensions)",
712
+ "dataset": "https://huggingface.co/datasets/stanfordnlp/SHP",
713
+ "dataset_revision": "e94b5f32602712d78ed494fe79105b1959396686",
714
+ "domains": [
715
+ "askacademia_train",
716
+ "askbaking_train",
717
+ "askcarguys_train",
718
+ "askphilosophy_train",
719
+ "legaladvice_train"
720
+ ],
721
+ "n_clients": 5,
722
+ "train_pairs_per_client": 120,
723
+ "eval_pairs_per_client": 40,
724
+ "rounds": 80,
725
+ "seeds": [
726
+ 42,
727
+ 43,
728
+ 44,
729
+ 45,
730
+ 46
731
+ ],
732
+ "learning_rate": 0.25,
733
+ "beta": 0.2,
734
+ "batch_size": 16,
735
+ "wall_time_seconds": 51.08550537499832,
736
+ "hardware": "Apple M1 CPU, 16 GB unified memory",
737
+ "hf_jobs_status": "blocked before submission: HTTP 402 insufficient credits",
738
+ "billed_cost_usd": 0.0
739
+ },
740
+ "fits": {
741
+ "participation_grad_vs_inverse_S": {
742
+ "slope": -1.490254080375279e-07,
743
+ "intercept": 0.0002957094078217976,
744
+ "r_squared": 0.7302089505595288
745
+ },
746
+ "staleness_grad_vs_q_max": {
747
+ "slope": 6.475459349008947e-08,
748
+ "intercept": 0.0002957158302631729,
749
+ "r_squared": 0.98409493928456
750
+ },
751
+ "topology_consensus_vs_inverse_gap": {
752
+ "slope": 0.0015142574989828385,
753
+ "intercept": -0.001139720595757755,
754
+ "r_squared": 0.6802636259472109
755
+ }
756
+ },
757
+ "summary": [
758
+ {
759
+ "study": "local_steps",
760
+ "value": "1",
761
+ "seeds": 5,
762
+ "final_grad_norm_sq_mean": 0.0002959844413176936,
763
+ "final_grad_norm_sq_std": 3.1242630621229745e-08,
764
+ "final_loss_mean": 0.692767434637361,
765
+ "final_loss_std": 2.9960266660448454e-05,
766
+ "final_consensus_mean": NaN,
767
+ "spectral_gap": NaN
768
+ },
769
+ {
770
+ "study": "local_steps",
771
+ "value": "3",
772
+ "seeds": 5,
773
+ "final_grad_norm_sq_mean": 0.0002956447370211676,
774
+ "final_grad_norm_sq_std": 7.143137704444013e-08,
775
+ "final_loss_mean": 0.6921181457214483,
776
+ "final_loss_std": 5.369632825832004e-05,
777
+ "final_consensus_mean": NaN,
778
+ "spectral_gap": NaN
779
+ },
780
+ {
781
+ "study": "local_steps",
782
+ "value": "6",
783
+ "seeds": 5,
784
+ "final_grad_norm_sq_mean": 0.0002951760642065067,
785
+ "final_grad_norm_sq_std": 6.683455807540559e-08,
786
+ "final_loss_mean": 0.6911896374824796,
787
+ "final_loss_std": 3.369466591631232e-05,
788
+ "final_consensus_mean": NaN,
789
+ "spectral_gap": NaN
790
+ },
791
+ {
792
+ "study": "participation",
793
+ "value": "1",
794
+ "seeds": 5,
795
+ "final_grad_norm_sq_mean": 0.0002955534092699556,
796
+ "final_grad_norm_sq_std": 2.5003321107893815e-07,
797
+ "final_loss_mean": 0.692018887625939,
798
+ "final_loss_std": 0.00019644787687949202,
799
+ "final_consensus_mean": NaN,
800
+ "spectral_gap": NaN
801
+ },
802
+ {
803
+ "study": "participation",
804
+ "value": "3",
805
+ "seeds": 5,
806
+ "final_grad_norm_sq_mean": 0.00029570157154861225,
807
+ "final_grad_norm_sq_std": 1.2928937159579134e-07,
808
+ "final_loss_mean": 0.6921678162240276,
809
+ "final_loss_std": 7.762641835153803e-05,
810
+ "final_consensus_mean": NaN,
811
+ "spectral_gap": NaN
812
+ },
813
+ {
814
+ "study": "participation",
815
+ "value": "5",
816
+ "seeds": 5,
817
+ "final_grad_norm_sq_mean": 0.0002956447370211676,
818
+ "final_grad_norm_sq_std": 7.143137704444013e-08,
819
+ "final_loss_mean": 0.6921181457214483,
820
+ "final_loss_std": 5.369632825832004e-05,
821
+ "final_consensus_mean": NaN,
822
+ "spectral_gap": NaN
823
+ },
824
+ {
825
+ "study": "staleness",
826
+ "value": "0",
827
+ "seeds": 5,
828
+ "final_grad_norm_sq_mean": 0.00029570157154861225,
829
+ "final_grad_norm_sq_std": 1.2928937159579134e-07,
830
+ "final_loss_mean": 0.6921678162240276,
831
+ "final_loss_std": 7.762641835153803e-05,
832
+ "final_consensus_mean": NaN,
833
+ "spectral_gap": NaN
834
+ },
835
+ {
836
+ "study": "staleness",
837
+ "value": "2",
838
+ "seeds": 5,
839
+ "final_grad_norm_sq_mean": 0.0002958691039744208,
840
+ "final_grad_norm_sq_std": 5.3671974588500576e-08,
841
+ "final_loss_mean": 0.6925704904970035,
842
+ "final_loss_std": 3.2284360529443266e-05,
843
+ "final_consensus_mean": NaN,
844
+ "spectral_gap": NaN
845
+ },
846
+ {
847
+ "study": "staleness",
848
+ "value": "5",
849
+ "seeds": 5,
850
+ "final_grad_norm_sq_mean": 0.00029603009742091614,
851
+ "final_grad_norm_sq_std": 3.3972548051372546e-08,
852
+ "final_loss_mean": 0.6928111896109521,
853
+ "final_loss_std": 2.615190936965446e-05,
854
+ "final_consensus_mean": NaN,
855
+ "spectral_gap": NaN
856
+ },
857
+ {
858
+ "study": "topology",
859
+ "value": "path",
860
+ "seeds": 5,
861
+ "final_grad_norm_sq_mean": 0.0002953077578322604,
862
+ "final_grad_norm_sq_std": 1.1483142849219158e-07,
863
+ "final_loss_mean": 0.6914850457852572,
864
+ "final_loss_std": 0.00010632600833795026,
865
+ "final_consensus_mean": 0.004072685849233602,
866
+ "spectral_gap": 0.23843311486114604
867
+ },
868
+ {
869
+ "study": "topology",
870
+ "value": "ring",
871
+ "seeds": 5,
872
+ "final_grad_norm_sq_mean": 0.0002953075298494851,
873
+ "final_grad_norm_sq_std": 1.1491135702150993e-07,
874
+ "final_loss_mean": 0.6914844965936869,
875
+ "final_loss_std": 0.00010642678367370458,
876
+ "final_consensus_mean": 0.0003035894361151224,
877
+ "spectral_gap": 0.709107334583345
878
+ },
879
+ {
880
+ "study": "topology",
881
+ "value": "star",
882
+ "seeds": 5,
883
+ "final_grad_norm_sq_mean": 0.00029530756477476273,
884
+ "final_grad_norm_sq_std": 1.1484830376721028e-07,
885
+ "final_loss_mean": 0.6914853768018828,
886
+ "final_loss_std": 0.00010632513365832594,
887
+ "final_consensus_mean": 0.005271681629193685,
888
+ "spectral_gap": 0.3599999999999999
889
+ },
890
+ {
891
+ "study": "topology",
892
+ "value": "complete",
893
+ "seeds": 5,
894
+ "final_grad_norm_sq_mean": 0.0002953078259770435,
895
+ "final_grad_norm_sq_std": 1.1488566697882272e-07,
896
+ "final_loss_mean": 0.6914845873743779,
897
+ "final_loss_std": 0.00010641988295715827,
898
+ "final_consensus_mean": 5.8427434688283515e-33,
899
+ "spectral_gap": 1.0
900
+ }
901
+ ]
902
+ }
903
+
904
+ ````
905
+
906
+
907
+ ---
908
+ <!-- trackio-cell
909
+ {"type": "artifact", "id": "cell_f350873e7c5d", "created_at": "2026-07-29T13:17:01+00:00", "title": "Artifact: trajectories.csv", "path": "empirical_outputs/trajectories.csv", "size": 708279, "artifact_type": "dataset", "auto": true}
910
+ -->
911
+ **📦 Artifact** `empirical_outputs/trajectories.csv` · dataset · 0.7 MB
912
+
913
+ https://huggingface.co/buckets/SabaPivot/repro-distributed-direct-preference-optimization-artifacts#logbook-files/empirical_outputs/trajectories.csv
914
+
915
+
916
+ ---
917
+ <!-- trackio-cell
918
+ {"type": "artifact", "id": "cell_195c7a9dc98b", "created_at": "2026-07-29T13:17:01+00:00", "title": "Artifact: summary.csv", "path": "empirical_outputs/summary.csv", "size": 1700, "artifact_type": "dataset", "auto": true}
919
+ -->
920
+ **📦 Artifact** `empirical_outputs/summary.csv` · dataset · 1.7 kB
921
+
922
+ https://huggingface.co/buckets/SabaPivot/repro-distributed-direct-preference-optimization-artifacts#logbook-files/empirical_outputs/summary.csv
923
+
924
+
925
+ ---
926
+ <!-- trackio-cell
927
+ {"type": "code", "id": "cell_b5b48e929423", "created_at": "2026-07-29T13:18:18+00:00", "title": "Run: uv empirical_repro.py (exit 0)", "command": ["uv", "run", "empirical_repro.py", "--output", "empirical_outputs", "--rounds", "80", "--seeds", "5", "--dimension", "1024", "--rows-per-domain", "160", "--train-rows", "120"], "exit_code": 0, "duration_s": 42.916}
928
+ -->
929
+ ````bash
930
+ $ uv run empirical_repro.py --output empirical_outputs --rounds 80 --seeds 5 --dimension 1024 --rows-per-domain 160 --train-rows 120
931
+ ````
932
+
933
+ exit 0 · 42.9s
934
+
935
+
936
+ ````python title=empirical_repro.py
937
+ # /// script
938
+ # requires-python = ">=3.11"
939
+ # dependencies = [
940
+ # "duckdb>=1.3",
941
+ # "numpy>=2.0",
942
+ # "plotly>=6.0",
943
+ # "trackio>=0.33.0",
944
+ # ]
945
+ # ///
946
+ """Scaled SHP reproduction using a log-linear DPO policy.
947
+
948
+ The paper's GPU experiment uses DistilGPT-2. Because the Hub Jobs canary is
949
+ blocked by account credit (HTTP 402), this local fallback uses the same N=5
950
+ client structure and real SHP preference pairs but a 1024-dimensional hashed
951
+ log-linear policy. This directly matches the policy class of the theory and
952
+ is intentionally labelled a scaled proxy, not a full LLM replication.
953
+ """
954
+
955
+ from __future__ import annotations
956
+
957
+ import argparse
958
+ import csv
959
+ import hashlib
960
+ import json
961
+ import math
962
+ import random
963
+ import re
964
+ import time
965
+ from collections import defaultdict
966
+ from pathlib import Path
967
+
968
+ import duckdb
969
+ import numpy as np
970
+ import plotly.graph_objects as go
971
+ from plotly.subplots import make_subplots
972
+ import trackio
973
+
974
+
975
+ PARQUET_URLS = [
976
+ "https://huggingface.co/datasets/stanfordnlp/SHP/resolve/"
977
+ "refs%2Fconvert%2Fparquet/default/train/0000.parquet",
978
+ "https://huggingface.co/datasets/stanfordnlp/SHP/resolve/"
979
+ "refs%2Fconvert%2Fparquet/default/train/0001.parquet",
980
+ ]
981
+ DOMAINS = [
982
+ "askacademia_train",
983
+ "askbaking_train",
984
+ "askcarguys_train",
985
+ "askphilosophy_train",
986
+ "legaladvice_train",
987
+ ]
988
+ TOKEN_RE = re.compile(r"[A-Za-z][A-Za-z0-9_'-]{1,}")
989
+
990
+
991
+ def load_shp(rows_per_domain: int) -> dict[int, list[dict]]:
992
+ urls = ", ".join(f"'{url}'" for url in PARQUET_URLS)
993
+ domains = ", ".join(f"'{domain}'" for domain in DOMAINS)
994
+ query = f"""
995
+ WITH ranked AS (
996
+ SELECT
997
+ domain,
998
+ human_ref_A,
999
+ human_ref_B,
1000
+ score_A,
1001
+ score_B,
1002
+ ROW_NUMBER() OVER (PARTITION BY domain ORDER BY post_id, c_root_id_A, c_root_id_B) AS rn
1003
+ FROM read_parquet([{urls}])
1004
+ WHERE domain IN ({domains}) AND score_A != score_B
1005
+ )
1006
+ SELECT domain, human_ref_A, human_ref_B, score_A, score_B
1007
+ FROM ranked
1008
+ WHERE rn <= {int(rows_per_domain)}
1009
+ ORDER BY domain, rn
1010
+ """
1011
+ rows = duckdb.sql(query).fetchall()
1012
+ grouped: dict[str, list[dict]] = defaultdict(list)
1013
+ for domain, text_a, text_b, score_a, score_b in rows:
1014
+ if score_a > score_b:
1015
+ chosen, rejected = text_a, text_b
1016
+ else:
1017
+ chosen, rejected = text_b, text_a
1018
+ if chosen and rejected and chosen != rejected:
1019
+ grouped[domain].append(
1020
+ {"chosen": chosen[-500:], "rejected": rejected[-500:]}
1021
+ )
1022
+ missing = [domain for domain in DOMAINS if len(grouped[domain]) < rows_per_domain]
1023
+ if missing:
1024
+ raise RuntimeError(f"insufficient rows for domains: {missing}")
1025
+ return {index: grouped[domain] for index, domain in enumerate(DOMAINS)}
1026
+
1027
+
1028
+ def stable_bucket(token: str, dimension: int) -> tuple[int, float]:
1029
+ digest = hashlib.blake2b(token.encode("utf-8"), digest_size=8).digest()
1030
+ value = int.from_bytes(digest, "little")
1031
+ return value % dimension, 1.0 if (value >> 63) == 0 else -1.0
1032
+
1033
+
1034
+ def text_vector(text: str, dimension: int) -> np.ndarray:
1035
+ vector = np.zeros(dimension, dtype=np.float64)
1036
+ tokens = TOKEN_RE.findall(text.lower())
1037
+ for token in tokens:
1038
+ index, sign = stable_bucket(token, dimension)
1039
+ vector[index] += sign
1040
+ norm = np.linalg.norm(vector)
1041
+ if norm > 0:
1042
+ vector /= norm
1043
+ return vector
1044
+
1045
+
1046
+ def featurize(
1047
+ data: dict[int, list[dict]],
1048
+ dimension: int,
1049
+ ) -> dict[int, np.ndarray]:
1050
+ result = {}
1051
+ for client, rows in data.items():
1052
+ result[client] = np.stack(
1053
+ [
1054
+ text_vector(row["chosen"], dimension)
1055
+ - text_vector(row["rejected"], dimension)
1056
+ for row in rows
1057
+ ]
1058
+ )
1059
+ return result
1060
+
1061
+
1062
+ def sigmoid_negative(z: np.ndarray) -> np.ndarray:
1063
+ return np.where(
1064
+ z >= 0,
1065
+ np.exp(-z) / (1.0 + np.exp(-z)),
1066
+ 1.0 / (1.0 + np.exp(z)),
1067
+ )
1068
+
1069
+
1070
+ def batch_gradient(
1071
+ theta: np.ndarray,
1072
+ features: np.ndarray,
1073
+ beta: float,
1074
+ ) -> np.ndarray:
1075
+ z = beta * (features @ theta)
1076
+ weights = -beta * sigmoid_negative(z)
1077
+ return (weights[:, None] * features).mean(axis=0)
1078
+
1079
+
1080
+ def batch_loss(theta: np.ndarray, features: np.ndarray, beta: float) -> float:
1081
+ z = beta * (features @ theta)
1082
+ return float(np.logaddexp(0.0, -z).mean())
1083
+
1084
+
1085
+ def evaluate(
1086
+ theta: np.ndarray,
1087
+ eval_features: dict[int, np.ndarray],
1088
+ beta: float,
1089
+ ) -> dict[str, float]:
1090
+ pooled = np.concatenate(list(eval_features.values()), axis=0)
1091
+ global_gradient = batch_gradient(theta, pooled, beta)
1092
+ client_gradients = np.stack(
1093
+ [batch_gradient(theta, rows, beta) for rows in eval_features.values()]
1094
+ )
1095
+ heterogeneity = float(
1096
+ np.mean(np.sum((client_gradients - global_gradient) ** 2, axis=1))
1097
+ )
1098
+ return {
1099
+ "loss": batch_loss(theta, pooled, beta),
1100
+ "grad_norm_sq": float(global_gradient @ global_gradient),
1101
+ "kappa_sq_proxy": heterogeneity,
1102
+ }
1103
+
1104
+
1105
+ def local_update(
1106
+ start: np.ndarray,
1107
+ features: np.ndarray,
1108
+ *,
1109
+ steps: int,
1110
+ batch_size: int,
1111
+ learning_rate: float,
1112
+ beta: float,
1113
+ rng: np.random.Generator,
1114
+ ) -> tuple[np.ndarray, float]:
1115
+ theta = start.copy()
1116
+ grad_norms = []
1117
+ for _ in range(steps):
1118
+ indexes = rng.choice(
1119
+ len(features),
1120
+ size=min(batch_size, len(features)),
1121
+ replace=False,
1122
+ )
1123
+ gradient = batch_gradient(theta, features[indexes], beta)
1124
+ theta -= learning_rate * gradient
1125
+ grad_norms.append(float(gradient @ gradient))
1126
+ return theta, float(np.mean(grad_norms))
1127
+
1128
+
1129
+ def run_fed(
1130
+ train: dict[int, np.ndarray],
1131
+ evaluation: dict[int, np.ndarray],
1132
+ *,
1133
+ rounds: int,
1134
+ local_steps: int,
1135
+ participation: int,
1136
+ q_max: int,
1137
+ learning_rate: float,
1138
+ beta: float,
1139
+ batch_size: int,
1140
+ seed: int,
1141
+ ) -> list[dict]:
1142
+ rng = np.random.default_rng(seed)
1143
+ n_clients = len(train)
1144
+ theta = np.zeros(train[0].shape[1], dtype=np.float64)
1145
+ history = [theta.copy()]
1146
+ rows = []
1147
+ for round_index in range(rounds):
1148
+ selected = rng.choice(n_clients, size=participation, replace=False)
1149
+ local_models = []
1150
+ local_grad_norms = []
1151
+ delays = []
1152
+ for client in selected:
1153
+ delay = int(rng.integers(0, q_max + 1)) if q_max else 0
1154
+ delay = min(delay, len(history) - 1)
1155
+ start = history[-1 - delay]
1156
+ local_model, grad_norm = local_update(
1157
+ start,
1158
+ train[int(client)],
1159
+ steps=local_steps,
1160
+ batch_size=batch_size,
1161
+ learning_rate=learning_rate,
1162
+ beta=beta,
1163
+ rng=rng,
1164
+ )
1165
+ local_models.append(local_model)
1166
+ local_grad_norms.append(grad_norm)
1167
+ delays.append(delay)
1168
+ theta = np.mean(local_models, axis=0)
1169
+ history.append(theta.copy())
1170
+ metrics = evaluate(theta, evaluation, beta)
1171
+ rows.append(
1172
+ {
1173
+ "round": round_index,
1174
+ **metrics,
1175
+ "train_grad_norm_sq": float(np.mean(local_grad_norms)),
1176
+ "mean_delay": float(np.mean(delays)),
1177
+ }
1178
+ )
1179
+ return rows
1180
+
1181
+
1182
+ def mixing_matrix(graph_type: str, n: int) -> tuple[np.ndarray, float, float]:
1183
+ if graph_type == "path":
1184
+ edges = [(i, i + 1) for i in range(n - 1)]
1185
+ elif graph_type == "ring":
1186
+ edges = [(i, (i + 1) % n) for i in range(n)]
1187
+ elif graph_type == "star":
1188
+ edges = [(0, i) for i in range(1, n)]
1189
+ elif graph_type == "complete":
1190
+ edges = [(i, j) for i in range(n) for j in range(i + 1, n)]
1191
+ else:
1192
+ raise ValueError(graph_type)
1193
+ degree = np.zeros(n, dtype=int)
1194
+ for i, j in edges:
1195
+ degree[i] += 1
1196
+ degree[j] += 1
1197
+ pi = np.zeros((n, n), dtype=np.float64)
1198
+ for i, j in edges:
1199
+ weight = 1.0 / (1 + max(degree[i], degree[j]))
1200
+ pi[i, j] = weight
1201
+ pi[j, i] = weight
1202
+ for i in range(n):
1203
+ pi[i, i] = 1.0 - pi[i].sum()
1204
+ eigenvalues = np.sort(np.abs(np.linalg.eigvalsh(pi)))[::-1]
1205
+ rho = float(eigenvalues[1])
1206
+ return pi, rho, 1.0 - rho**2
1207
+
1208
+
1209
+ def run_decentralized(
1210
+ train: dict[int, np.ndarray],
1211
+ evaluation: dict[int, np.ndarray],
1212
+ *,
1213
+ rounds: int,
1214
+ local_steps: int,
1215
+ topology: str,
1216
+ learning_rate: float,
1217
+ beta: float,
1218
+ batch_size: int,
1219
+ seed: int,
1220
+ ) -> list[dict]:
1221
+ rng = np.random.default_rng(seed)
1222
+ n_clients = len(train)
1223
+ dimension = train[0].shape[1]
1224
+ agents = np.zeros((n_clients, dimension), dtype=np.float64)
1225
+ pi, rho, spectral_gap = mixing_matrix(topology, n_clients)
1226
+ rows = []
1227
+ for round_index in range(rounds):
1228
+ local_grad_norms = []
1229
+ for client in range(n_clients):
1230
+ agents[client], grad_norm = local_update(
1231
+ agents[client],
1232
+ train[client],
1233
+ steps=local_steps,
1234
+ batch_size=batch_size,
1235
+ learning_rate=learning_rate,
1236
+ beta=beta,
1237
+ rng=rng,
1238
+ )
1239
+ local_grad_norms.append(grad_norm)
1240
+ consensus_before = float(
1241
+ np.mean(np.sum((agents - agents.mean(axis=0)) ** 2, axis=1))
1242
+ )
1243
+ agents = pi @ agents
1244
+ mean_theta = agents.mean(axis=0)
1245
+ consensus_after = float(
1246
+ np.mean(np.sum((agents - mean_theta) ** 2, axis=1))
1247
+ )
1248
+ metrics = evaluate(mean_theta, evaluation, beta)
1249
+ rows.append(
1250
+ {
1251
+ "round": round_index,
1252
+ **metrics,
1253
+ "train_grad_norm_sq": float(np.mean(local_grad_norms)),
1254
+ "consensus_before": consensus_before,
1255
+ "consensus_after": consensus_after,
1256
+ "rho": rho,
1257
+ "spectral_gap": spectral_gap,
1258
+ }
1259
+ )
1260
+ return rows
1261
+
1262
+
1263
+ def linear_fit(x: list[float], y: list[float]) -> dict[str, float]:
1264
+ coefficients = np.polyfit(np.asarray(x), np.asarray(y), 1)
1265
+ predicted = np.polyval(coefficients, x)
1266
+ residual = float(np.sum((np.asarray(y) - predicted) ** 2))
1267
+ total = float(np.sum((np.asarray(y) - np.mean(y)) ** 2))
1268
+ return {
1269
+ "slope": float(coefficients[0]),
1270
+ "intercept": float(coefficients[1]),
1271
+ "r_squared": 1.0 - residual / total if total > 0 else 1.0,
1272
+ }
1273
+
1274
+
1275
+ def summarize(
1276
+ trajectories: list[dict],
1277
+ configurations: list[dict],
1278
+ ) -> tuple[list[dict], dict]:
1279
+ grouped: dict[tuple, list[dict]] = defaultdict(list)
1280
+ for row in trajectories:
1281
+ grouped[(row["study"], row["value"], row["seed"])].append(row)
1282
+
1283
+ final_by_config: dict[tuple, list[dict]] = defaultdict(list)
1284
+ for (study, value, seed), rows in grouped.items():
1285
+ final_by_config[(study, value)].append(max(rows, key=lambda row: row["round"]))
1286
+
1287
+ summary_rows = []
1288
+ for config in configurations:
1289
+ key = (config["study"], str(config["value"]))
1290
+ finals = final_by_config[key]
1291
+ row = {
1292
+ "study": key[0],
1293
+ "value": key[1],
1294
+ "seeds": len(finals),
1295
+ "final_grad_norm_sq_mean": float(
1296
+ np.mean([item["grad_norm_sq"] for item in finals])
1297
+ ),
1298
+ "final_grad_norm_sq_std": float(
1299
+ np.std([item["grad_norm_sq"] for item in finals])
1300
+ ),
1301
+ "final_loss_mean": float(np.mean([item["loss"] for item in finals])),
1302
+ "final_loss_std": float(np.std([item["loss"] for item in finals])),
1303
+ "final_consensus_mean": float(
1304
+ np.mean([item.get("consensus_after", math.nan) for item in finals])
1305
+ )
1306
+ if "consensus_after" in finals[0]
1307
+ else math.nan,
1308
+ "spectral_gap": float(finals[0].get("spectral_gap", math.nan)),
1309
+ }
1310
+ summary_rows.append(row)
1311
+
1312
+ participation = [
1313
+ row for row in summary_rows if row["study"] == "participation"
1314
+ ]
1315
+ staleness = [row for row in summary_rows if row["study"] == "staleness"]
1316
+ topology = [row for row in summary_rows if row["study"] == "topology"]
1317
+ fits = {
1318
+ "participation_grad_vs_inverse_S": linear_fit(
1319
+ [1.0 / float(row["value"]) for row in participation],
1320
+ [row["final_grad_norm_sq_mean"] for row in participation],
1321
+ ),
1322
+ "participation_seed_std_vs_inverse_S": linear_fit(
1323
+ [1.0 / float(row["value"]) for row in participation],
1324
+ [row["final_grad_norm_sq_std"] for row in participation],
1325
+ ),
1326
+ "staleness_grad_vs_q_max": linear_fit(
1327
+ [float(row["value"]) for row in staleness],
1328
+ [row["final_grad_norm_sq_mean"] for row in staleness],
1329
+ ),
1330
+ "topology_consensus_vs_inverse_gap": linear_fit(
1331
+ [1.0 / row["spectral_gap"] for row in topology],
1332
+ [row["final_consensus_mean"] for row in topology],
1333
+ ),
1334
+ }
1335
+ return summary_rows, fits
1336
+
1337
+
1338
+ def write_csv(path: Path, rows: list[dict]) -> None:
1339
+ keys = sorted({key for row in rows for key in row})
1340
+ with path.open("w", newline="", encoding="utf-8") as handle:
1341
+ writer = csv.DictWriter(handle, fieldnames=keys)
1342
+ writer.writeheader()
1343
+ writer.writerows(rows)
1344
+
1345
+
1346
+ def make_figure(summary_rows: list[dict], output: Path) -> None:
1347
+ fig = make_subplots(
1348
+ rows=2,
1349
+ cols=2,
1350
+ subplot_titles=(
1351
+ "Local steps E",
1352
+ "Participation S",
1353
+ "Actual bounded staleness q_max",
1354
+ "Topology and consensus",
1355
+ ),
1356
+ )
1357
+ panels = [
1358
+ ("local_steps", 1, 1),
1359
+ ("participation", 1, 2),
1360
+ ("staleness", 2, 1),
1361
+ ]
1362
+ for study, row_index, col_index in panels:
1363
+ rows = [row for row in summary_rows if row["study"] == study]
1364
+ x = [float(row["value"]) for row in rows]
1365
+ y = [row["final_grad_norm_sq_mean"] for row in rows]
1366
+ error = [row["final_grad_norm_sq_std"] for row in rows]
1367
+ fig.add_trace(
1368
+ go.Scatter(
1369
+ x=x,
1370
+ y=y,
1371
+ error_y={"type": "data", "array": error},
1372
+ mode="lines+markers",
1373
+ name=study,
1374
+ ),
1375
+ row=row_index,
1376
+ col=col_index,
1377
+ )
1378
+ topology_rows = [
1379
+ row for row in summary_rows if row["study"] == "topology"
1380
+ ]
1381
+ fig.add_trace(
1382
+ go.Scatter(
1383
+ x=[1.0 / row["spectral_gap"] for row in topology_rows],
1384
+ y=[row["final_consensus_mean"] for row in topology_rows],
1385
+ mode="markers+text",
1386
+ text=[row["value"] for row in topology_rows],
1387
+ textposition="top center",
1388
+ name="topology",
1389
+ ),
1390
+ row=2,
1391
+ col=2,
1392
+ )
1393
+ fig.update_layout(
1394
+ title="Scaled SHP log-linear DPO reproduction (N=5, mean ± std over seeds)",
1395
+ template="plotly_white",
1396
+ width=1200,
1397
+ height=800,
1398
+ )
1399
+ fig.update_xaxes(title_text="E", row=1, col=1)
1400
+ fig.update_xaxes(title_text="S", row=1, col=2)
1401
+ fig.update_xaxes(title_text="q_max", row=2, col=1)
1402
+ fig.update_xaxes(title_text="1 / (1 - ρ²)", row=2, col=2)
1403
+ fig.update_yaxes(title_text="final gradient norm²", row=1, col=1)
1404
+ fig.update_yaxes(title_text="final gradient norm²", row=1, col=2)
1405
+ fig.update_yaxes(title_text="final gradient norm²", row=2, col=1)
1406
+ fig.update_yaxes(title_text="final consensus error", row=2, col=2)
1407
+ fig.write_html(output, include_plotlyjs="cdn")
1408
+
1409
+
1410
+ def main() -> None:
1411
+ parser = argparse.ArgumentParser()
1412
+ parser.add_argument("--output", type=Path, default=Path("empirical_outputs"))
1413
+ parser.add_argument("--rounds", type=int, default=80)
1414
+ parser.add_argument("--seeds", type=int, default=5)
1415
+ parser.add_argument("--dimension", type=int, default=1024)
1416
+ parser.add_argument("--rows-per-domain", type=int, default=160)
1417
+ parser.add_argument("--train-rows", type=int, default=120)
1418
+ parser.add_argument("--learning-rate", type=float, default=0.25)
1419
+ parser.add_argument("--beta", type=float, default=0.2)
1420
+ parser.add_argument("--batch-size", type=int, default=16)
1421
+ args = parser.parse_args()
1422
+ args.output.mkdir(parents=True, exist_ok=True)
1423
+ started = time.perf_counter()
1424
+
1425
+ raw = load_shp(args.rows_per_domain)
1426
+ features = featurize(raw, args.dimension)
1427
+ train = {client: values[: args.train_rows] for client, values in features.items()}
1428
+ evaluation = {
1429
+ client: values[args.train_rows :] for client, values in features.items()
1430
+ }
1431
+
1432
+ configurations = [
1433
+ *[
1434
+ {
1435
+ "study": "local_steps",
1436
+ "value": e,
1437
+ "kind": "fed",
1438
+ "local_steps": e,
1439
+ "participation": 5,
1440
+ "q_max": 0,
1441
+ }
1442
+ for e in [1, 3, 6]
1443
+ ],
1444
+ *[
1445
+ {
1446
+ "study": "participation",
1447
+ "value": s,
1448
+ "kind": "fed",
1449
+ "local_steps": 3,
1450
+ "participation": s,
1451
+ "q_max": 0,
1452
+ }
1453
+ for s in [1, 3, 5]
1454
+ ],
1455
+ *[
1456
+ {
1457
+ "study": "staleness",
1458
+ "value": q,
1459
+ "kind": "fed",
1460
+ "local_steps": 3,
1461
+ "participation": 3,
1462
+ "q_max": q,
1463
+ }
1464
+ for q in [0, 2, 5]
1465
+ ],
1466
+ *[
1467
+ {
1468
+ "study": "topology",
1469
+ "value": topology,
1470
+ "kind": "decentralized",
1471
+ "local_steps": 5,
1472
+ "topology": topology,
1473
+ }
1474
+ for topology in ["path", "ring", "star", "complete"]
1475
+ ],
1476
+ ]
1477
+
1478
+ trajectories: list[dict] = []
1479
+ for config_index, config in enumerate(configurations):
1480
+ for seed in range(42, 42 + args.seeds):
1481
+ if config["kind"] == "fed":
1482
+ rows = run_fed(
1483
+ train,
1484
+ evaluation,
1485
+ rounds=args.rounds,
1486
+ local_steps=config["local_steps"],
1487
+ participation=config["participation"],
1488
+ q_max=config["q_max"],
1489
+ learning_rate=args.learning_rate,
1490
+ beta=args.beta,
1491
+ batch_size=args.batch_size,
1492
+ seed=seed,
1493
+ )
1494
+ else:
1495
+ rows = run_decentralized(
1496
+ train,
1497
+ evaluation,
1498
+ rounds=args.rounds,
1499
+ local_steps=config["local_steps"],
1500
+ topology=config["topology"],
1501
+ learning_rate=args.learning_rate,
1502
+ beta=args.beta,
1503
+ batch_size=args.batch_size,
1504
+ seed=seed,
1505
+ )
1506
+ for row in rows:
1507
+ trajectories.append(
1508
+ {
1509
+ "study": config["study"],
1510
+ "value": str(config["value"]),
1511
+ "seed": seed,
1512
+ **row,
1513
+ }
1514
+ )
1515
+ print(
1516
+ f"[{config_index + 1}/{len(configurations)}] "
1517
+ f"{config['study']}={config['value']} complete"
1518
+ )
1519
+
1520
+ summary_rows, fits = summarize(trajectories, configurations)
1521
+ duration = time.perf_counter() - started
1522
+ metadata = {
1523
+ "scope": "scaled local proxy; real SHP, N=5, log-linear policy",
1524
+ "full_paper_backbone": "distilgpt2 (~82M)",
1525
+ "reproduction_policy": f"hashed log-linear ({args.dimension} dimensions)",
1526
+ "dataset": "https://huggingface.co/datasets/stanfordnlp/SHP",
1527
+ "dataset_revision": "e94b5f32602712d78ed494fe79105b1959396686",
1528
+ "domains": DOMAINS,
1529
+ "n_clients": 5,
1530
+ "train_pairs_per_client": args.train_rows,
1531
+ "eval_pairs_per_client": args.rows_per_domain - args.train_rows,
1532
+ "rounds": args.rounds,
1533
+ "seeds": list(range(42, 42 + args.seeds)),
1534
+ "learning_rate": args.learning_rate,
1535
+ "beta": args.beta,
1536
+ "batch_size": args.batch_size,
1537
+ "wall_time_seconds": duration,
1538
+ "hardware": "Apple M1 CPU, 16 GB unified memory",
1539
+ "hf_jobs_status": "blocked before submission: HTTP 402 insufficient credits",
1540
+ "billed_cost_usd": 0.0,
1541
+ }
1542
+ report = {
1543
+ "metadata": metadata,
1544
+ "fits": fits,
1545
+ "summary": summary_rows,
1546
+ }
1547
+
1548
+ trajectories_path = args.output / "trajectories.csv"
1549
+ summary_path = args.output / "summary.csv"
1550
+ report_path = args.output / "results.json"
1551
+ figure_path = args.output / "ablation_summary.html"
1552
+ write_csv(trajectories_path, trajectories)
1553
+ write_csv(summary_path, summary_rows)
1554
+ report_path.write_text(json.dumps(report, indent=2), encoding="utf-8")
1555
+ make_figure(summary_rows, figure_path)
1556
+
1557
+ trackio.init(
1558
+ project="ddpo-shp-scaled-reproduction",
1559
+ name="n5-loglinear-five-seeds",
1560
+ config=metadata,
1561
+ )
1562
+ for step, row in enumerate(summary_rows):
1563
+ trackio.log(
1564
+ {
1565
+ "summary/final_grad_norm_sq": row["final_grad_norm_sq_mean"],
1566
+ "summary/final_loss": row["final_loss_mean"],
1567
+ "summary/config_index": step,
1568
+ },
1569
+ step=step,
1570
+ )
1571
+ trackio.log(
1572
+ {
1573
+ "fits/participation_slope": fits[
1574
+ "participation_grad_vs_inverse_S"
1575
+ ]["slope"],
1576
+ "fits/participation_seed_std_slope": fits[
1577
+ "participation_seed_std_vs_inverse_S"
1578
+ ]["slope"],
1579
+ "fits/staleness_slope": fits["staleness_grad_vs_q_max"]["slope"],
1580
+ "fits/topology_slope": fits[
1581
+ "topology_consensus_vs_inverse_gap"
1582
+ ]["slope"],
1583
+ "wall_time_seconds": duration,
1584
+ },
1585
+ step=len(summary_rows),
1586
+ )
1587
+ trackio.log_artifact(
1588
+ args.output,
1589
+ name="ddpo-shp-scaled-results",
1590
+ type="dataset",
1591
+ aliases=["reproduction"],
1592
+ )
1593
+ trackio.finish()
1594
+
1595
+ print(json.dumps(report, indent=2))
1596
+
1597
+
1598
+ if __name__ == "__main__":
1599
+ main()
1600
+
1601
+ ````
1602
+
1603
+
1604
+ ````output
1605
+ [1/13] local_steps=1 complete
1606
+ [2/13] local_steps=3 complete
1607
+ [3/13] local_steps=6 complete
1608
+ [4/13] participation=1 complete
1609
+ [5/13] participation=3 complete
1610
+ [6/13] participation=5 complete
1611
+ [7/13] staleness=0 complete
1612
+ [8/13] staleness=2 complete
1613
+ [9/13] staleness=5 complete
1614
+ [10/13] topology=path complete
1615
+ [11/13] topology=ring complete
1616
+ [12/13] topology=star complete
1617
+ [13/13] topology=complete complete
1618
+ * Trackio project initialized: ddpo-shp-scaled-reproduction
1619
+ * Trackio metrics logged to: /Users/test/.cache/huggingface/trackio
1620
+ * View dashboard by running in your terminal:
1621
+ trackio show --project "ddpo-shp-scaled-reproduction"
1622
+ * or by running in Python: trackio.show(project="ddpo-shp-scaled-reproduction")
1623
+ * Created new run: n5-loglinear-five-seeds
1624
+ * Run finished. Uploading logs to Trackio (please wait...)
1625
+ {
1626
+ "metadata": {
1627
+ "scope": "scaled local proxy; real SHP, N=5, log-linear policy",
1628
+ "full_paper_backbone": "distilgpt2 (~82M)",
1629
+ "reproduction_policy": "hashed log-linear (1024 dimensions)",
1630
+ "dataset": "https://huggingface.co/datasets/stanfordnlp/SHP",
1631
+ "dataset_revision": "e94b5f32602712d78ed494fe79105b1959396686",
1632
+ "domains": [
1633
+ "askacademia_train",
1634
+ "askbaking_train",
1635
+ "askcarguys_train",
1636
+ "askphilosophy_train",
1637
+ "legaladvice_train"
1638
+ ],
1639
+ "n_clients": 5,
1640
+ "train_pairs_per_client": 120,
1641
+ "eval_pairs_per_client": 40,
1642
+ "rounds": 80,
1643
+ "seeds": [
1644
+ 42,
1645
+ 43,
1646
+ 44,
1647
+ 45,
1648
+ 46
1649
+ ],
1650
+ "learning_rate": 0.25,
1651
+ "beta": 0.2,
1652
+ "batch_size": 16,
1653
+ "wall_time_seconds": 41.70772958299494,
1654
+ "hardware": "Apple M1 CPU, 16 GB unified memory",
1655
+ "hf_jobs_status": "blocked before submission: HTTP 402 insufficient credits",
1656
+ "billed_cost_usd": 0.0
1657
+ },
1658
+ "fits": {
1659
+ "participation_grad_vs_inverse_S": {
1660
+ "slope": -1.490254080375279e-07,
1661
+ "intercept": 0.0002957094078217976,
1662
+ "r_squared": 0.7302089505595288
1663
+ },
1664
+ "participation_seed_std_vs_inverse_S": {
1665
+ "slope": 2.0965986244041204e-07,
1666
+ "intercept": 4.309183465906812e-08,
1667
+ "r_squared": 0.9724121551177207
1668
+ },
1669
+ "staleness_grad_vs_q_max": {
1670
+ "slope": 6.475459349008947e-08,
1671
+ "intercept": 0.0002957158302631729,
1672
+ "r_squared": 0.98409493928456
1673
+ },
1674
+ "topology_consensus_vs_inverse_gap": {
1675
+ "slope": 0.0015142574989828385,
1676
+ "intercept": -0.001139720595757755,
1677
+ "r_squared": 0.6802636259472109
1678
+ }
1679
+ },
1680
+ "summary": [
1681
+ {
1682
+ "study": "local_steps",
1683
+ "value": "1",
1684
+ "seeds": 5,
1685
+ "final_grad_norm_sq_mean": 0.0002959844413176936,
1686
+ "final_grad_norm_sq_std": 3.1242630621229745e-08,
1687
+ "final_loss_mean": 0.692767434637361,
1688
+ "final_loss_std": 2.9960266660448454e-05,
1689
+ "final_consensus_mean": NaN,
1690
+ "spectral_gap": NaN
1691
+ },
1692
+ {
1693
+ "study": "local_steps",
1694
+ "value": "3",
1695
+ "seeds": 5,
1696
+ "final_grad_norm_sq_mean": 0.0002956447370211676,
1697
+ "final_grad_norm_sq_std": 7.143137704444013e-08,
1698
+ "final_loss_mean": 0.6921181457214483,
1699
+ "final_loss_std": 5.369632825832004e-05,
1700
+ "final_consensus_mean": NaN,
1701
+ "spectral_gap": NaN
1702
+ },
1703
+ {
1704
+ "study": "local_steps",
1705
+ "value": "6",
1706
+ "seeds": 5,
1707
+ "final_grad_norm_sq_mean": 0.0002951760642065067,
1708
+ "final_grad_norm_sq_std": 6.683455807540559e-08,
1709
+ "final_loss_mean": 0.6911896374824796,
1710
+ "final_loss_std": 3.369466591631232e-05,
1711
+ "final_consensus_mean": NaN,
1712
+ "spectral_gap": NaN
1713
+ },
1714
+ {
1715
+ "study": "participation",
1716
+ "value": "1",
1717
+ "seeds": 5,
1718
+ "final_grad_norm_sq_mean": 0.0002955534092699556,
1719
+ "final_grad_norm_sq_std": 2.5003321107893815e-07,
1720
+ "final_loss_mean": 0.692018887625939,
1721
+ "final_loss_std": 0.00019644787687949202,
1722
+ "final_consensus_mean": NaN,
1723
+ "spectral_gap": NaN
1724
+ },
1725
+ {
1726
+ "study": "participation",
1727
+ "value": "3",
1728
+ "seeds": 5,
1729
+ "final_grad_norm_sq_mean": 0.00029570157154861225,
1730
+ "final_grad_norm_sq_std": 1.2928937159579134e-07,
1731
+ "final_loss_mean": 0.6921678162240276,
1732
+ "final_loss_std": 7.762641835153803e-05,
1733
+ "final_consensus_mean": NaN,
1734
+ "spectral_gap": NaN
1735
+ },
1736
+ {
1737
+ "study": "participation",
1738
+ "value": "5",
1739
+ "seeds": 5,
1740
+ "final_grad_norm_sq_mean": 0.0002956447370211676,
1741
+ "final_grad_norm_sq_std": 7.143137704444013e-08,
1742
+ "final_loss_mean": 0.6921181457214483,
1743
+ "final_loss_std": 5.369632825832004e-05,
1744
+ "final_consensus_mean": NaN,
1745
+ "spectral_gap": NaN
1746
+ },
1747
+ {
1748
+ "study": "staleness",
1749
+ "value": "0",
1750
+ "seeds": 5,
1751
+ "final_grad_norm_sq_mean": 0.00029570157154861225,
1752
+ "final_grad_norm_sq_std": 1.2928937159579134e-07,
1753
+ "final_loss_mean": 0.6921678162240276,
1754
+ "final_loss_std": 7.762641835153803e-05,
1755
+ "final_consensus_mean": NaN,
1756
+ "spectral_gap": NaN
1757
+ },
1758
+ {
1759
+ "study": "staleness",
1760
+ "value": "2",
1761
+ "seeds": 5,
1762
+ "final_grad_norm_sq_mean": 0.0002958691039744208,
1763
+ "final_grad_norm_sq_std": 5.3671974588500576e-08,
1764
+ "final_loss_mean": 0.6925704904970035,
1765
+ "final_loss_std": 3.2284360529443266e-05,
1766
+ "final_consensus_mean": NaN,
1767
+ "spectral_gap": NaN
1768
+ },
1769
+ {
1770
+ "study": "staleness",
1771
+ "value": "5",
1772
+ "seeds": 5,
1773
+ "final_grad_norm_sq_mean": 0.00029603009742091614,
1774
+ "final_grad_norm_sq_std": 3.3972548051372546e-08,
1775
+ "final_loss_mean": 0.6928111896109521,
1776
+ "final_loss_std": 2.615190936965446e-05,
1777
+ "final_consensus_mean": NaN,
1778
+ "spectral_gap": NaN
1779
+ },
1780
+ {
1781
+ "study": "topology",
1782
+ "value": "path",
1783
+ "seeds": 5,
1784
+ "final_grad_norm_sq_mean": 0.0002953077578322604,
1785
+ "final_grad_norm_sq_std": 1.1483142849219158e-07,
1786
+ "final_loss_mean": 0.6914850457852572,
1787
+ "final_loss_std": 0.00010632600833795026,
1788
+ "final_consensus_mean": 0.004072685849233602,
1789
+ "spectral_gap": 0.23843311486114604
1790
+ },
1791
+ {
1792
+ "study": "topology",
1793
+ "value": "ring",
1794
+ "seeds": 5,
1795
+ "final_grad_norm_sq_mean": 0.0002953075298494851,
1796
+ "final_grad_norm_sq_std": 1.1491135702150993e-07,
1797
+ "final_loss_mean": 0.6914844965936869,
1798
+ "final_loss_std": 0.00010642678367370458,
1799
+ "final_consensus_mean": 0.0003035894361151224,
1800
+ "spectral_gap": 0.709107334583345
1801
+ },
1802
+ {
1803
+ "study": "topology",
1804
+ "value": "star",
1805
+ "seeds": 5,
1806
+ "final_grad_norm_sq_mean": 0.00029530756477476273,
1807
+ "final_grad_norm_sq_std": 1.1484830376721028e-07,
1808
+ "final_loss_mean": 0.6914853768018828,
1809
+ "final_loss_std": 0.00010632513365832594,
1810
+ "final_consensus_mean": 0.005271681629193685,
1811
+ "spectral_gap": 0.3599999999999999
1812
+ },
1813
+ {
1814
+ "study": "topology",
1815
+ "value": "complete",
1816
+ "seeds": 5,
1817
+ "final_grad_norm_sq_mean": 0.0002953078259770435,
1818
+ "final_grad_norm_sq_std": 1.1488566697882272e-07,
1819
+ "final_loss_mean": 0.6914845873743779,
1820
+ "final_loss_std": 0.00010641988295715827,
1821
+ "final_consensus_mean": 5.8427434688283515e-33,
1822
+ "spectral_gap": 1.0
1823
+ }
1824
+ ]
1825
+ }
1826
+
1827
+ ````
1828
+
1829
+
1830
+ ---
1831
+ <!-- trackio-cell
1832
+ {"type": "artifact", "id": "cell_329a94b543a3", "created_at": "2026-07-29T13:18:18+00:00", "title": "Artifact: trajectories.csv", "path": "empirical_outputs/trajectories.csv", "size": 708279, "artifact_type": "dataset", "auto": true}
1833
+ -->
1834
+ **📦 Artifact** `empirical_outputs/trajectories.csv` · dataset · 0.7 MB
1835
+
1836
+ https://huggingface.co/buckets/SabaPivot/repro-distributed-direct-preference-optimization-artifacts#logbook-files/empirical_outputs/trajectories.csv
1837
+
1838
+
1839
+ ---
1840
+ <!-- trackio-cell
1841
+ {"type": "artifact", "id": "cell_0b28e6f98950", "created_at": "2026-07-29T13:18:18+00:00", "title": "Artifact: summary.csv", "path": "empirical_outputs/summary.csv", "size": 1700, "artifact_type": "dataset", "auto": true}
1842
+ -->
1843
+ **📦 Artifact** `empirical_outputs/summary.csv` · dataset · 1.7 kB
1844
+
1845
+ https://huggingface.co/buckets/SabaPivot/repro-distributed-direct-preference-optimization-artifacts#logbook-files/empirical_outputs/summary.csv
1846
+
1847
+
1848
+ ---
1849
+ <!-- trackio-cell
1850
+ {"type": "markdown", "id": "cell_70f58acbaf16", "created_at": "2026-07-29T13:21:26+00:00", "title": "Finding"}
1851
+ -->
1852
+ **Verdict: mixed, partial support at reduced model scale.** The final run used five distinct [SHP](https://huggingface.co/datasets/stanfordnlp/SHP) domains as non-IID clients, 120 train and 40 held-out preference pairs per client, 80 rounds, and seeds 42–46. A 1024-D hashed log-linear policy replaced the paper [DistilGPT-2](https://huggingface.co/distilbert/distilgpt2) backbone; this matches the theory policy class but is not a full LLM replication.
1853
+
1854
+ | Effect | Result |
1855
+ |---|---|
1856
+ | Local steps `E=1,3,6` | Final gradient norm² decreased `2.95984e-4 → 2.95645e-4 → 2.95176e-4`; supported in the computation-dominated regime. |
1857
+ | Participation `S=1,3,5` | Mean final gap was not monotone, contradicting the strong mean-floor plot claim at this scale. Across-seed std did fall `2.50e-7 → 1.29e-7 → 0.714e-7`, with positive fit versus `1/S` and `R²=0.972`; variance effect supported. |
1858
+ | Staleness `q=0,2,5` | Mean final gap rose monotonically; positive linear fit `R²=0.984`; supported qualitatively. |
1859
+ | Topology | Path `0.00407`, ring `0.000304`, star `0.00527`, complete `≈0` final consensus error; fit versus `1/(1−ρ²)` has `R²=0.680`. Partial support with a star anomaly, also noted in the paper. |
1860
+
1861
+ The [official repository at commit 97ef67e](https://github.com/starkjiang/distributed_dpo/tree/97ef67ef9e44df8b75aeef55e4ecaa6141d6ade7) does not reproduce its own documented defaults: `configs/default.yaml` and README say N=5, R=80, E=3, S=3, while runtime `distributed_dpo/config.py` has N=3, R=5, E=1, S=2 and never loads the YAML. Its staleness implementation ignores the magnitude of `q_max`. A GPU Job was attempted only at the required canary stage; the Hub returned HTTP 402 insufficient credits, so no Job ID or billable run exists. Local final wall time was 42.9 s on Apple M1; billed cost $0. Dataset revisions inspected: [SHP e94b5f3](https://huggingface.co/datasets/stanfordnlp/SHP/tree/e94b5f32602712d78ed494fe79105b1959396686) and [HH-RLHF 09be8c5](https://huggingface.co/datasets/Anthropic/hh-rlhf/tree/09be8c5bbc57cb3887f3a9732ad6aa7ec602a1fa).
1862
+
1863
+
1864
+ ---
1865
+ <!-- trackio-cell
1866
+ {"type": "figure", "id": "cell_b69168af59f5", "created_at": "2026-07-29T13:22:53+00:00", "title": "SHP ablation summary"}
1867
+ -->
1868
+ ````html
1869
+ <html>
1870
+ <head><meta charset="utf-8" /></head>
1871
+ <body>
1872
+ <div style="height:800px; width:1200px;"> <script>window.PlotlyConfig = {MathJaxConfig: 'local'};</script>
1873
+ <script charset="utf-8" src="https://cdn.plot.ly/plotly-3.7.0.min.js" integrity="sha256-jvTGqxNp8AGWEcvNLVuKr+8j5dGe9Yw51LQkmDH+IYA=" crossorigin="anonymous"></script> <div id="b352e0d4-5a60-4aab-8cc8-9aec40f8aab0" class="plotly-graph-div" style="height:100%; width:100%;"></div> <script> window.PLOTLYENV=window.PLOTLYENV || {}; if (document.getElementById("b352e0d4-5a60-4aab-8cc8-9aec40f8aab0")) { Plotly.newPlot( "b352e0d4-5a60-4aab-8cc8-9aec40f8aab0", [{"error_y":{"array":[3.1242630621229745e-8,7.143137704444013e-8,6.683455807540559e-8],"type":"data"},"mode":"lines+markers","name":"local_steps","x":[1.0,3.0,6.0],"y":[0.0002959844413176936,0.0002956447370211676,0.0002951760642065067],"type":"scatter","xaxis":"x","yaxis":"y"},{"error_y":{"array":[2.5003321107893815e-7,1.2928937159579134e-7,7.143137704444013e-8],"type":"data"},"mode":"lines+markers","name":"participation","x":[1.0,3.0,5.0],"y":[0.0002955534092699556,0.00029570157154861225,0.0002956447370211676],"type":"scatter","xaxis":"x2","yaxis":"y2"},{"error_y":{"array":[1.2928937159579134e-7,5.3671974588500576e-8,3.3972548051372546e-8],"type":"data"},"mode":"lines+markers","name":"staleness","x":[0.0,2.0,5.0],"y":[0.00029570157154861225,0.0002958691039744208,0.00029603009742091614],"type":"scatter","xaxis":"x3","yaxis":"y3"},{"mode":"markers+text","name":"topology","text":["path","ring","star","complete"],"textposition":"top center","x":[4.194048299802485,1.4102237436136194,2.7777777777777786,1.0],"y":[0.004072685849233602,0.0003035894361151224,0.005271681629193685,5.8427434688283515e-33],"type":"scatter","xaxis":"x4","yaxis":"y4"}], {"template":{"data":{"barpolar":[{"marker":{"line":{"color":"white","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"barpolar"}],"bar":[{"error_x":{"color":"#2a3f5f"},"error_y":{"color":"#2a3f5f"},"marker":{"line":{"color":"white","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"bar"}],"carpet":[{"aaxis":{"endlinecolor":"#2a3f5f","gridcolor":"#C8D4E3","linecolor":"#C8D4E3","minorgridcolor":"#C8D4E3","startlinecolor":"#2a3f5f"},"baxis":{"endlinecolor":"#2a3f5f","gridcolor":"#C8D4E3","linecolor":"#C8D4E3","minorgridcolor":"#C8D4E3","startlinecolor":"#2a3f5f"},"type":"carpet"}],"choropleth":[{"colorbar":{"outlinewidth":0,"ticks":""},"type":"choropleth"}],"contourcarpet":[{"colorbar":{"outlinewidth":0,"ticks":""},"type":"contourcarpet"}],"contour":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"contour"}],"heatmap":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"heatmap"}],"histogram2dcontour":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"histogram2dcontour"}],"histogram2d":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"histogram2d"}],"histogram":[{"marker":{"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"histogram"}],"mesh3d":[{"colorbar":{"outlinewidth":0,"ticks":""},"type":"mesh3d"}],"parcoords":[{"line":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"parcoords"}],"pie":[{"automargin":true,"type":"pie"}],"scatter3d":[{"line":{"colorbar":{"outlinewidth":0,"ticks":""}},"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatter3d"}],"scattercarpet":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattercarpet"}],"scattergeo":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattergeo"}],"scattergl":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattergl"}],"scattermapbox":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattermapbox"}],"scattermap":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattermap"}],"scatterpolargl":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatterpolargl"}],"scatterpolar":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatterpolar"}],"scatter":[{"fillpattern":{"fillmode":"overlay","size":10,"solidity":0.2},"type":"scatter"}],"scatterternary":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatterternary"}],"surface":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"surface"}],"table":[{"cells":{"fill":{"color":"#EBF0F8"},"line":{"color":"white"}},"header":{"fill":{"color":"#C8D4E3"},"line":{"color":"white"}},"type":"table"}]},"layout":{"annotationdefaults":{"arrowcolor":"#2a3f5f","arrowhead":0,"arrowwidth":1},"autotypenumbers":"strict","coloraxis":{"colorbar":{"outlinewidth":0,"ticks":""}},"colorscale":{"diverging":[[0,"#8e0152"],[0.1,"#c51b7d"],[0.2,"#de77ae"],[0.3,"#f1b6da"],[0.4,"#fde0ef"],[0.5,"#f7f7f7"],[0.6,"#e6f5d0"],[0.7,"#b8e186"],[0.8,"#7fbc41"],[0.9,"#4d9221"],[1,"#276419"]],"sequential":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"sequentialminus":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]},"colorway":["#636efa","#EF553B","#00cc96","#ab63fa","#FFA15A","#19d3f3","#FF6692","#B6E880","#FF97FF","#FECB52"],"font":{"color":"#2a3f5f"},"geo":{"bgcolor":"white","lakecolor":"white","landcolor":"white","showlakes":true,"showland":true,"subunitcolor":"#C8D4E3"},"hoverlabel":{"align":"left"},"hovermode":"closest","mapbox":{"style":"light"},"paper_bgcolor":"white","plot_bgcolor":"white","polar":{"angularaxis":{"gridcolor":"#EBF0F8","linecolor":"#EBF0F8","ticks":""},"bgcolor":"white","radialaxis":{"gridcolor":"#EBF0F8","linecolor":"#EBF0F8","ticks":""}},"scene":{"xaxis":{"backgroundcolor":"white","gridcolor":"#DFE8F3","gridwidth":2,"linecolor":"#EBF0F8","showbackground":true,"ticks":"","zerolinecolor":"#EBF0F8"},"yaxis":{"backgroundcolor":"white","gridcolor":"#DFE8F3","gridwidth":2,"linecolor":"#EBF0F8","showbackground":true,"ticks":"","zerolinecolor":"#EBF0F8"},"zaxis":{"backgroundcolor":"white","gridcolor":"#DFE8F3","gridwidth":2,"linecolor":"#EBF0F8","showbackground":true,"ticks":"","zerolinecolor":"#EBF0F8"}},"shapedefaults":{"line":{"color":"#2a3f5f"}},"ternary":{"aaxis":{"gridcolor":"#DFE8F3","linecolor":"#A2B1C6","ticks":""},"baxis":{"gridcolor":"#DFE8F3","linecolor":"#A2B1C6","ticks":""},"bgcolor":"white","caxis":{"gridcolor":"#DFE8F3","linecolor":"#A2B1C6","ticks":""}},"title":{"x":0.05},"xaxis":{"automargin":true,"gridcolor":"#EBF0F8","linecolor":"#EBF0F8","ticks":"","title":{"standoff":15},"zerolinecolor":"#EBF0F8","zerolinewidth":2},"yaxis":{"automargin":true,"gridcolor":"#EBF0F8","linecolor":"#EBF0F8","ticks":"","title":{"standoff":15},"zerolinecolor":"#EBF0F8","zerolinewidth":2}}},"xaxis":{"anchor":"y","domain":[0.0,0.45],"title":{"text":"E"}},"yaxis":{"anchor":"x","domain":[0.625,1.0],"title":{"text":"final gradient norm²"}},"xaxis2":{"anchor":"y2","domain":[0.55,1.0],"title":{"text":"S"}},"yaxis2":{"anchor":"x2","domain":[0.625,1.0],"title":{"text":"final gradient norm²"}},"xaxis3":{"anchor":"y3","domain":[0.0,0.45],"title":{"text":"q_max"}},"yaxis3":{"anchor":"x3","domain":[0.0,0.375],"title":{"text":"final gradient norm²"}},"xaxis4":{"anchor":"y4","domain":[0.55,1.0],"title":{"text":"1 \u002f (1 - ρ²)"}},"yaxis4":{"anchor":"x4","domain":[0.0,0.375],"title":{"text":"final consensus error"}},"annotations":[{"font":{"size":16},"showarrow":false,"text":"Local steps E","x":0.225,"xanchor":"center","xref":"paper","y":1.0,"yanchor":"bottom","yref":"paper"},{"font":{"size":16},"showarrow":false,"text":"Participation S","x":0.775,"xanchor":"center","xref":"paper","y":1.0,"yanchor":"bottom","yref":"paper"},{"font":{"size":16},"showarrow":false,"text":"Actual bounded staleness q_max","x":0.225,"xanchor":"center","xref":"paper","y":0.375,"yanchor":"bottom","yref":"paper"},{"font":{"size":16},"showarrow":false,"text":"Topology and consensus","x":0.775,"xanchor":"center","xref":"paper","y":0.375,"yanchor":"bottom","yref":"paper"}],"title":{"text":"Scaled SHP log-linear DPO reproduction (N=5, mean ± std over seeds)"},"width":1200,"height":800}, {"responsive": true} ) }; </script> </div>
1874
+ </body>
1875
+ </html>
1876
+ ````
1877
+
1878
+ ````raw
1879
+ {
1880
+ "metadata": {
1881
+ "scope": "scaled local proxy; real SHP, N=5, log-linear policy",
1882
+ "full_paper_backbone": "distilgpt2 (~82M)",
1883
+ "reproduction_policy": "hashed log-linear (1024 dimensions)",
1884
+ "dataset": "https://huggingface.co/datasets/stanfordnlp/SHP",
1885
+ "dataset_revision": "e94b5f32602712d78ed494fe79105b1959396686",
1886
+ "domains": [
1887
+ "askacademia_train",
1888
+ "askbaking_train",
1889
+ "askcarguys_train",
1890
+ "askphilosophy_train",
1891
+ "legaladvice_train"
1892
+ ],
1893
+ "n_clients": 5,
1894
+ "train_pairs_per_client": 120,
1895
+ "eval_pairs_per_client": 40,
1896
+ "rounds": 80,
1897
+ "seeds": [
1898
+ 42,
1899
+ 43,
1900
+ 44,
1901
+ 45,
1902
+ 46
1903
+ ],
1904
+ "learning_rate": 0.25,
1905
+ "beta": 0.2,
1906
+ "batch_size": 16,
1907
+ "wall_time_seconds": 41.70772958299494,
1908
+ "hardware": "Apple M1 CPU, 16 GB unified memory",
1909
+ "hf_jobs_status": "blocked before submission: HTTP 402 insufficient credits",
1910
+ "billed_cost_usd": 0.0
1911
+ },
1912
+ "fits": {
1913
+ "participation_grad_vs_inverse_S": {
1914
+ "slope": -1.490254080375279e-07,
1915
+ "intercept": 0.0002957094078217976,
1916
+ "r_squared": 0.7302089505595288
1917
+ },
1918
+ "participation_seed_std_vs_inverse_S": {
1919
+ "slope": 2.0965986244041204e-07,
1920
+ "intercept": 4.309183465906812e-08,
1921
+ "r_squared": 0.9724121551177207
1922
+ },
1923
+ "staleness_grad_vs_q_max": {
1924
+ "slope": 6.475459349008947e-08,
1925
+ "intercept": 0.0002957158302631729,
1926
+ "r_squared": 0.98409493928456
1927
+ },
1928
+ "topology_consensus_vs_inverse_gap": {
1929
+ "slope": 0.0015142574989828385,
1930
+ "intercept": -0.001139720595757755,
1931
+ "r_squared": 0.6802636259472109
1932
+ }
1933
+ },
1934
+ "summary": [
1935
+ {
1936
+ "study": "local_steps",
1937
+ "value": "1",
1938
+ "seeds": 5,
1939
+ "final_grad_norm_sq_mean": 0.0002959844413176936,
1940
+ "final_grad_norm_sq_std": 3.1242630621229745e-08,
1941
+ "final_loss_mean": 0.692767434637361,
1942
+ "final_loss_std": 2.9960266660448454e-05,
1943
+ "final_consensus_mean": NaN,
1944
+ "spectral_gap": NaN
1945
+ },
1946
+ {
1947
+ "study": "local_steps",
1948
+ "value": "3",
1949
+ "seeds": 5,
1950
+ "final_grad_norm_sq_mean": 0.0002956447370211676,
1951
+ "final_grad_norm_sq_std": 7.143137704444013e-08,
1952
+ "final_loss_mean": 0.6921181457214483,
1953
+ "final_loss_std": 5.369632825832004e-05,
1954
+ "final_consensus_mean": NaN,
1955
+ "spectral_gap": NaN
1956
+ },
1957
+ {
1958
+ "study": "local_steps",
1959
+ "value": "6",
1960
+ "seeds": 5,
1961
+ "final_grad_norm_sq_mean": 0.0002951760642065067,
1962
+ "final_grad_norm_sq_std": 6.683455807540559e-08,
1963
+ "final_loss_mean": 0.6911896374824796,
1964
+ "final_loss_std": 3.369466591631232e-05,
1965
+ "final_consensus_mean": NaN,
1966
+ "spectral_gap": NaN
1967
+ },
1968
+ {
1969
+ "study": "participation",
1970
+ "value": "1",
1971
+ "seeds": 5,
1972
+ "final_grad_norm_sq_mean": 0.0002955534092699556,
1973
+ "final_grad_norm_sq_std": 2.5003321107893815e-07,
1974
+ "final_loss_mean": 0.692018887625939,
1975
+ "final_loss_std": 0.00019644787687949202,
1976
+ "final_consensus_mean": NaN,
1977
+ "spectral_gap": NaN
1978
+ },
1979
+ {
1980
+ "study": "participation",
1981
+ "value": "3",
1982
+ "seeds": 5,
1983
+ "final_grad_norm_sq_mean": 0.00029570157154861225,
1984
+ "final_grad_norm_sq_std": 1.2928937159579134e-07,
1985
+ "final_loss_mean": 0.6921678162240276,
1986
+ "final_loss_std": 7.762641835153803e-05,
1987
+ "final_consensus_mean": NaN,
1988
+ "spectral_gap": NaN
1989
+ },
1990
+ {
1991
+ "study": "participation",
1992
+ "value": "5",
1993
+ "seeds": 5,
1994
+ "final_grad_norm_sq_mean": 0.0002956447370211676,
1995
+ "final_grad_norm_sq_std": 7.143137704444013e-08,
1996
+ "final_loss_mean": 0.6921181457214483,
1997
+ "final_loss_std": 5.369632825832004e-05,
1998
+ "final_consensus_mean": NaN,
1999
+ "spectral_gap": NaN
2000
+ },
2001
+ {
2002
+ "study": "staleness",
2003
+ "value": "0",
2004
+ "seeds": 5,
2005
+ "final_grad_norm_sq_mean": 0.00029570157154861225,
2006
+ "final_grad_norm_sq_std": 1.2928937159579134e-07,
2007
+ "final_loss_mean": 0.6921678162240276,
2008
+ "final_loss_std": 7.762641835153803e-05,
2009
+ "final_consensus_mean": NaN,
2010
+ "spectral_gap": NaN
2011
+ },
2012
+ {
2013
+ "study": "staleness",
2014
+ "value": "2",
2015
+ "seeds": 5,
2016
+ "final_grad_norm_sq_mean": 0.0002958691039744208,
2017
+ "final_grad_norm_sq_std": 5.3671974588500576e-08,
2018
+ "final_loss_mean": 0.6925704904970035,
2019
+ "final_loss_std": 3.2284360529443266e-05,
2020
+ "final_consensus_mean": NaN,
2021
+ "spectral_gap": NaN
2022
+ },
2023
+ {
2024
+ "study": "staleness",
2025
+ "value": "5",
2026
+ "seeds": 5,
2027
+ "final_grad_norm_sq_mean": 0.00029603009742091614,
2028
+ "final_grad_norm_sq_std": 3.3972548051372546e-08,
2029
+ "final_loss_mean": 0.6928111896109521,
2030
+ "final_loss_std": 2.615190936965446e-05,
2031
+ "final_consensus_mean": NaN,
2032
+ "spectral_gap": NaN
2033
+ },
2034
+ {
2035
+ "study": "topology",
2036
+ "value": "path",
2037
+ "seeds": 5,
2038
+ "final_grad_norm_sq_mean": 0.0002953077578322604,
2039
+ "final_grad_norm_sq_std": 1.1483142849219158e-07,
2040
+ "final_loss_mean": 0.6914850457852572,
2041
+ "final_loss_std": 0.00010632600833795026,
2042
+ "final_consensus_mean": 0.004072685849233602,
2043
+ "spectral_gap": 0.23843311486114604
2044
+ },
2045
+ {
2046
+ "study": "topology",
2047
+ "value": "ring",
2048
+ "seeds": 5,
2049
+ "final_grad_norm_sq_mean": 0.0002953075298494851,
2050
+ "final_grad_norm_sq_std": 1.1491135702150993e-07,
2051
+ "final_loss_mean": 0.6914844965936869,
2052
+ "final_loss_std": 0.00010642678367370458,
2053
+ "final_consensus_mean": 0.0003035894361151224,
2054
+ "spectral_gap": 0.709107334583345
2055
+ },
2056
+ {
2057
+ "study": "topology",
2058
+ "value": "star",
2059
+ "seeds": 5,
2060
+ "final_grad_norm_sq_mean": 0.00029530756477476273,
2061
+ "final_grad_norm_sq_std": 1.1484830376721028e-07,
2062
+ "final_loss_mean": 0.6914853768018828,
2063
+ "final_loss_std": 0.00010632513365832594,
2064
+ "final_consensus_mean": 0.005271681629193685,
2065
+ "spectral_gap": 0.3599999999999999
2066
+ },
2067
+ {
2068
+ "study": "topology",
2069
+ "value": "complete",
2070
+ "seeds": 5,
2071
+ "final_grad_norm_sq_mean": 0.0002953078259770435,
2072
+ "final_grad_norm_sq_std": 1.1488566697882272e-07,
2073
+ "final_loss_mean": 0.6914845873743779,
2074
+ "final_loss_std": 0.00010641988295715827,
2075
+ "final_consensus_mean": 5.8427434688283515e-33,
2076
+ "spectral_gap": 1.0
2077
+ }
2078
+ ]
2079
+ }
2080
+ ````
pages/conclusion/page.md ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Conclusion
2
+
3
+
4
+ ---
5
+ <!-- trackio-cell
6
+ {"type": "markdown", "id": "cell_553cfaf65499", "created_at": "2026-07-29T13:21:27+00:00", "title": "Conclusion"}
7
+ -->
8
+ **Overall finding: the paper contains promising mechanisms but the six requested claims are not reproducible as a rigorous package from the released materials.** Claim 5 has correct statement-level rate algebra and a double-precision spectral-recursion audit. Claim 2 is only correct under the narrow interpretation that full participation removes client-sampling amplification, since stochastic variance `2Lηζ²_g/N` remains. Claims 1 and 3 have displayed statements but no active proofs and identifiable draft gaps; Claim 4 has a quantifier/monotonicity problem and no actual DPO hard-instance proof. Claim 6 receives mixed proxy support on real SHP: local steps, bounded staleness, and most topology effects agree, but the mean participation gap does not.
9
+
10
+ Reproducibility notes:
11
+
12
+ - Paper: [arXiv 2605.20696](https://arxiv.org/abs/2605.20696); [OpenReview ljNZyrAlaa](https://openreview.net/forum?id=ljNZyrAlaa).
13
+ - Code audit pinned to [GitHub commit 97ef67e](https://github.com/starkjiang/distributed_dpo/tree/97ef67ef9e44df8b75aeef55e4ecaa6141d6ade7).
14
+ - Data: [SHP](https://huggingface.co/datasets/stanfordnlp/SHP) and inspected [HH-RLHF](https://huggingface.co/datasets/Anthropic/hh-rlhf).
15
+ - Model named by the paper: [DistilGPT-2](https://huggingface.co/distilbert/distilgpt2); not executed in this credit-blocked reproduction.
16
+ - Exact commands, scripts, outputs, CSV trajectories, raw JSON, and interactive figures are captured in this logbook. The published artifact Bucket and private trace dataset are linked automatically after publication.
17
+ - No Hugging Face Job exists: the mandatory canary failed before creation with HTTP 402. This is an infrastructure limitation, not a successful zero-cost Job.
18
+ - The local proxy intentionally improves two design issues: clients are split by domain rather than randomly, and `q_max` samples an actual bounded delay rather than acting as a Boolean.
pages/executive-summary/page.md ADDED
The diff for this file is too large to render. See raw diff
 
pages/index.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Reproduction: Distributed Direct Preference Optimization
2
+
3
+ ## Pages
4
+
5
+ | Page |
6
+ | --- |
7
+ | [Executive summary](#/executive-summary) |
8
+ | [Claim 1: FedDPO partial participation bound](#/claim-1-feddpo-partial-participation-bound) |
9
+ | [Claim 2: Full participation corollary](#/claim-2-full-participation-corollary) |
10
+ | [Claim 3: Staleness penalty](#/claim-3-staleness-penalty) |
11
+ | [Claim 4: FedDPO lower bound](#/claim-4-feddpo-lower-bound) |
12
+ | [Claim 5: DecDPO spectral rate](#/claim-5-decdpo-spectral-rate) |
13
+ | [Claim 6: SHP numerical results](#/claim-6-shp-numerical-results) |
14
+ | [Conclusion](#/conclusion) |
trackio-logo-light.png ADDED
trackio-logo.png ADDED
trackio-wordmark-dark.png ADDED
workspace.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema_version": 1,
3
+ "file_count": 0,
4
+ "total_size": 0,
5
+ "files": [],
6
+ "hub_refs": [
7
+ {
8
+ "url": "https://huggingface.co/datasets/stanfordnlp/SHP",
9
+ "type": "Datasets",
10
+ "label": "stanfordnlp/SHP"
11
+ },
12
+ {
13
+ "url": "https://huggingface.co/distilbert/distilgpt2",
14
+ "type": "Models",
15
+ "label": "distilbert/distilgpt2"
16
+ },
17
+ {
18
+ "url": "https://huggingface.co/datasets/Anthropic/hh-rlhf",
19
+ "type": "Datasets",
20
+ "label": "Anthropic/hh-rlhf"
21
+ },
22
+ {
23
+ "url": "https://huggingface.co/buckets/SabaPivot/repro-distributed-direct-preference-optimization-artifacts#logbook-files/theory_outputs/bound_terms.csv",
24
+ "type": "Buckets",
25
+ "label": "SabaPivot/repro-distributed-direct-preference-optimization-artifacts"
26
+ }
27
+ ],
28
+ "reference_only": true
29
+ }