cwLeeDev commited on
Commit
9be8fa9
ยท
verified ยท
1 Parent(s): 52585d7

Add reproducible LiteRT Colab conversion bundle

Browse files
MANIFEST.json CHANGED
@@ -1,11 +1,11 @@
1
  ๏ปฟ{
2
- "schema": "aiflow-hf-research-snapshot-v8",
3
- "generated_at": "2026-07-23T20:04:13.5124140Z",
4
  "track": "R_noncommercial_plus_rejected_P_proxy",
5
  "product_validation": false,
6
  "public_release": true,
7
  "contains_raw_dataset": false,
8
- "tests": "282 passed",
9
  "retracted_paths": [
10
  "models/auxiliary/boundary_auxiliary_head.pt",
11
  "models/auxiliary/seed17/boundary_joint_delta.pt",
@@ -30,28 +30,28 @@
30
  },
31
  {
32
  "path": "reports/RESEARCH_REPORT.md",
33
- "bytes": 33159,
34
- "sha256": "19c06349b4316cb538a48fbe1cf5fccaaa386ca215615ae6ca53bfaefc0995df"
35
- },
36
- {
37
- "path": "scripts/evaluate_math_ink_06_p_boundary_device_stress.py",
38
- "bytes": 10444,
39
- "sha256": "bdd75047c4887a6e53720f1c84d224545844cdbd9b0239826a605d578b3c92a6"
40
  },
41
  {
42
  "path": "scripts/evaluate_crohme_tray_joint_selector.py",
43
  "bytes": 11671,
44
  "sha256": "58c175a95cdeb9fc764fb96594a76888122b8f118f2f26da1283e743b1a7cd04"
45
  },
 
 
 
 
 
46
  {
47
  "path": "scripts/audit_math_ink_06_local_baseline_overmerge.py",
48
  "bytes": 11900,
49
  "sha256": "f32a18c8e7d3a0ea36379293e6be88bfb509b7df99846c38149fb8458e6cc994"
50
  },
51
  {
52
- "path": "reports/online_casecontext_seed31.json",
53
- "bytes": 33576,
54
- "sha256": "30e6ca83aa4c04d1d257b0221fea7ac726f8e09e91906fdecbc12a2bd4d4d34b"
55
  },
56
  {
57
  "path": "reports/online_casecontext_seed17.json",
@@ -64,14 +64,14 @@
64
  "sha256": "27b31da71b9f39f7ded38aa1ce3694911af140d22854543d4ae2b5f9e709b479"
65
  },
66
  {
67
- "path": "reports/online_casecontext_seed47.json",
68
- "bytes": 33727,
69
- "sha256": "c3dfd328d9ff0a55eed2cb74657ad87e5e164968fddd7fae8c8fd92e137a4557"
70
  },
71
  {
72
- "path": "reports/p_boundary_joint_seed17.json",
73
- "bytes": 6920,
74
- "sha256": "7af3bb995041e5ea621d3c9535f3552e264860f12a7e6145637849f5cd0220a8"
75
  },
76
  {
77
  "path": "reports/p_boundary_device_stress_sharedfix_3seed.json",
@@ -83,6 +83,11 @@
83
  "bytes": 16685,
84
  "sha256": "9d250e5c75d4f104971551c07a702810bf94fb5b6e5e587035a624933766356e"
85
  },
 
 
 
 
 
86
  {
87
  "path": "src/math_ink_06.py",
88
  "bytes": 54708,
@@ -119,9 +124,9 @@
119
  "sha256": "8cc2dc1e13b56c21d53a76b9b863cdf5c8f8dd315da6eb18c1bd2b5b87c1a616"
120
  },
121
  {
122
- "path": "scripts/summarize_math_ink_06_p_boundary_joint.py",
123
- "bytes": 4580,
124
- "sha256": "c6ce78f521232bdb7e967b0c358232a72dd3f79747f9e32eb240d9bb10c6a422"
125
  },
126
  {
127
  "path": "scripts/preflight_math_ink_06_p_formula.py",
@@ -130,18 +135,18 @@
130
  },
131
  {
132
  "path": "scripts/export_math_ink_06_litert.py",
133
- "bytes": 8637,
134
- "sha256": "e96b30ded57d33d4505b065c1ac69c5ac7db43618c0eb7f3b7fb787b6fcaf1f6"
135
  },
136
  {
137
- "path": "scripts/sweep_math_ink_06_component_competition_guard.py",
138
- "bytes": 9084,
139
- "sha256": "e2dee616e2c6ebdc32281624889017685648b7076515242e04a1452d98d1c7da"
140
  },
141
  {
142
- "path": "scripts/train_math_ink_06_p_boundary_auxiliary.py",
143
- "bytes": 20688,
144
- "sha256": "70ca8a05dcbd48e79d812cd7ff44ee3d8479055f898143daa6d6ea7580b2973e"
145
  },
146
  {
147
  "path": "scripts/train_math_ink_06_boundary_behavior_guard.py",
@@ -154,14 +159,9 @@
154
  "sha256": "28cfc7b9b695256cfa9199d912d1d938d402292b88e9411d42fddb3508446d3b"
155
  },
156
  {
157
- "path": "exports/seed17/online.pt2",
158
- "bytes": 9192090,
159
- "sha256": "d8a4fa6a631d5f1f4e8871b5c551d647530312260caace3cab4bca01004f7891"
160
- },
161
- {
162
- "path": "exports/seed17/export_manifest.json",
163
- "bytes": 1030,
164
- "sha256": "48a224d03fb2e8f1c4fcb6ca3f61456521f4437b4cfc845572111cd84cb93833"
165
  },
166
  {
167
  "path": "contracts/aiflow_p_formula_v1.schema.json",
@@ -169,25 +169,40 @@
169
  "sha256": "c78c8a612d2f24a5c09fe1aa158b365481102869c3f84b3a5510a895b416c424"
170
  },
171
  {
172
- "path": "exports/seed17/raster.pt2",
173
- "bytes": 9465689,
174
- "sha256": "419301d67bbfe7b807e1ea676a33827c749f632300392d54eaf48c701ca07512"
175
  },
176
  {
177
- "path": "models/seed17/online_adapter.pt",
178
- "bytes": 2875654,
179
- "sha256": "8ea224d7bc107a4c8b6cb91c9de6598000dae463d3e6d39480c081ea728cc87f"
180
  },
181
  {
182
- "path": "models/seed17/behavior_role_head.pt",
183
- "bytes": 75794,
184
- "sha256": "57ced82a2600bd342bba68ba0636ecc3fc98c3dc461e26a132b5264d0a3d95bd"
185
  },
186
  {
187
  "path": "models/seed17/base_378.pt",
188
  "bytes": 8638290,
189
  "sha256": "f3b0e53b50e41cf57d4881c501371b35648cf049d603984a0edf6148c2b62bd7"
190
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  {
192
  "path": "pyproject.snapshot.toml",
193
  "bytes": 669,
@@ -200,13 +215,13 @@
200
  },
201
  {
202
  "path": "MODEL_INDEX.json",
203
- "bytes": 2406,
204
- "sha256": "2419245ba889772d562d524999e9d14f801e70d87ce75e38231722eb5aecd697"
205
  },
206
  {
207
  "path": "README.md",
208
- "bytes": 11248,
209
- "sha256": "d102514283972dbddc061848a1fb33a5d4f581dea0b43dc9eb3f4f8de46d5885"
210
  },
211
  {
212
  "path": "assets/stroke_encoding.svg",
@@ -223,11 +238,6 @@
223
  "bytes": 824191,
224
  "sha256": "18aff472c1bdb25a519b1553364229c9869aed1f36a9d663cf88a1bc808fab8f"
225
  },
226
- {
227
- "path": "reports/behavior_role_seed47.json",
228
- "bytes": 30091,
229
- "sha256": "3d4dcbe2f2b0afb483f992dacd62aab1089bdc41d55cc259bcb7fc21b5294db0"
230
- },
231
  {
232
  "path": "reports/behavior_role_seed31.json",
233
  "bytes": 45412,
@@ -239,14 +249,14 @@
239
  "sha256": "e2072fec1b1065777f0e9218c48d27f096b22f5545e7cc8d9962939282719561"
240
  },
241
  {
242
- "path": "reports/boundary_behavior_guard_report.json",
243
- "bytes": 72053,
244
- "sha256": "f55a230a9f0892b9676c71db46e9e527862b3d8da776dc43db767ce7a0f6a5c7"
245
  },
246
  {
247
- "path": "reports/local_baseline_overmerge_audit.json",
248
- "bytes": 1237409,
249
- "sha256": "33a9ccafebd559ca8b173f011e9a0a6f495d37b8fdb2693673663479d3457071"
250
  },
251
  {
252
  "path": "reports/local_baseline_guard_report.json",
@@ -259,14 +269,14 @@
259
  "sha256": "4d3db5f71e5008381dd072f2c10aafb22e08f96e23f2a32f82a3a46abd9c70b9"
260
  },
261
  {
262
- "path": "models/seed31/online_adapter.pt",
263
- "bytes": 2875654,
264
- "sha256": "fedbbab64cea6755e76e0633367ee45fbcc1d5726bb9dfdb7c5fe4bbc968bdf2"
265
  },
266
  {
267
- "path": "models/seed31/behavior_role_head.pt",
268
- "bytes": 75794,
269
- "sha256": "b7c22cf49320b90a6fd103d39cc3ff08ea7734ab83275aad66e2b529b749d9ae"
270
  },
271
  {
272
  "path": "models/seed31/base_378.pt",
@@ -274,24 +284,34 @@
274
  "sha256": "e3c57bc8cbbd4542cacf7ab9200a9031dcdc5ee63ffbd6ad6c9f7ef4f0a64f37"
275
  },
276
  {
277
- "path": "models/seed47/base_378.pt",
278
- "bytes": 8638290,
279
- "sha256": "4131b21f6b00471c1c5a82aee4a2da45e599e42dc742fc1dd0dd8afb970f8939"
280
  },
281
  {
282
- "path": "reports/behavior_role_3seed_summary.json",
283
- "bytes": 3567,
284
- "sha256": "b16049218d2d006d952f59f55642fa9f3fbfbc3cc3a730fbd7b075f507c3b251"
285
  },
286
  {
287
- "path": "models/seed47/online_adapter.pt",
288
- "bytes": 2876038,
289
- "sha256": "46394eeebad3d27d3178f78e693e160700ba3f454d79044714b835bc51c07056"
290
  },
291
  {
292
  "path": "models/seed47/behavior_role_head.pt",
293
  "bytes": 75794,
294
  "sha256": "f28fad4e0289e3bc9400829f41aba9bbd75c43d445b7462a84aadbdbe635c10b"
 
 
 
 
 
 
 
 
 
 
295
  }
296
  ]
297
  }
 
1
  ๏ปฟ{
2
+ "schema": "aiflow-hf-research-snapshot-v9",
3
+ "generated_at": "2026-07-23T20:15:19.8122156Z",
4
  "track": "R_noncommercial_plus_rejected_P_proxy",
5
  "product_validation": false,
6
  "public_release": true,
7
  "contains_raw_dataset": false,
8
+ "tests": "284 passed",
9
  "retracted_paths": [
10
  "models/auxiliary/boundary_auxiliary_head.pt",
11
  "models/auxiliary/seed17/boundary_joint_delta.pt",
 
30
  },
31
  {
32
  "path": "reports/RESEARCH_REPORT.md",
33
+ "bytes": 34116,
34
+ "sha256": "c2c1f9fc166525912d34f421b80b3f2662b18ec4a4d47934990c37b9432223e8"
 
 
 
 
 
35
  },
36
  {
37
  "path": "scripts/evaluate_crohme_tray_joint_selector.py",
38
  "bytes": 11671,
39
  "sha256": "58c175a95cdeb9fc764fb96594a76888122b8f118f2f26da1283e743b1a7cd04"
40
  },
41
+ {
42
+ "path": "scripts/build_math_ink_06_litert_colab_bundle.py",
43
+ "bytes": 6862,
44
+ "sha256": "f008635ab59344005f65f1394f56275e4c92610cd25781a2d95243d060010718"
45
+ },
46
  {
47
  "path": "scripts/audit_math_ink_06_local_baseline_overmerge.py",
48
  "bytes": 11900,
49
  "sha256": "f32a18c8e7d3a0ea36379293e6be88bfb509b7df99846c38149fb8458e6cc994"
50
  },
51
  {
52
+ "path": "reports/p_boundary_joint_seed17.json",
53
+ "bytes": 6920,
54
+ "sha256": "7af3bb995041e5ea621d3c9535f3552e264860f12a7e6145637849f5cd0220a8"
55
  },
56
  {
57
  "path": "reports/online_casecontext_seed17.json",
 
64
  "sha256": "27b31da71b9f39f7ded38aa1ce3694911af140d22854543d4ae2b5f9e709b479"
65
  },
66
  {
67
+ "path": "reports/local_baseline_overmerge_audit.json",
68
+ "bytes": 1237409,
69
+ "sha256": "33a9ccafebd559ca8b173f011e9a0a6f495d37b8fdb2693673663479d3457071"
70
  },
71
  {
72
+ "path": "reports/online_casecontext_seed31.json",
73
+ "bytes": 33576,
74
+ "sha256": "30e6ca83aa4c04d1d257b0221fea7ac726f8e09e91906fdecbc12a2bd4d4d34b"
75
  },
76
  {
77
  "path": "reports/p_boundary_device_stress_sharedfix_3seed.json",
 
83
  "bytes": 16685,
84
  "sha256": "9d250e5c75d4f104971551c07a702810bf94fb5b6e5e587035a624933766356e"
85
  },
86
+ {
87
+ "path": "reports/online_casecontext_seed47.json",
88
+ "bytes": 33727,
89
+ "sha256": "c3dfd328d9ff0a55eed2cb74657ad87e5e164968fddd7fae8c8fd92e137a4557"
90
+ },
91
  {
92
  "path": "src/math_ink_06.py",
93
  "bytes": 54708,
 
124
  "sha256": "8cc2dc1e13b56c21d53a76b9b863cdf5c8f8dd315da6eb18c1bd2b5b87c1a616"
125
  },
126
  {
127
+ "path": "scripts/train_math_ink_06_p_boundary_auxiliary.py",
128
+ "bytes": 20688,
129
+ "sha256": "70ca8a05dcbd48e79d812cd7ff44ee3d8479055f898143daa6d6ea7580b2973e"
130
  },
131
  {
132
  "path": "scripts/preflight_math_ink_06_p_formula.py",
 
135
  },
136
  {
137
  "path": "scripts/export_math_ink_06_litert.py",
138
+ "bytes": 11876,
139
+ "sha256": "3d5362341c29ef31493969ccf833c21cb1d4e02d8ccf9ccd217b038091f9e43c"
140
  },
141
  {
142
+ "path": "scripts/evaluate_math_ink_06_p_boundary_device_stress.py",
143
+ "bytes": 10444,
144
+ "sha256": "bdd75047c4887a6e53720f1c84d224545844cdbd9b0239826a605d578b3c92a6"
145
  },
146
  {
147
+ "path": "scripts/summarize_math_ink_06_p_boundary_joint.py",
148
+ "bytes": 4580,
149
+ "sha256": "c6ce78f521232bdb7e967b0c358232a72dd3f79747f9e32eb240d9bb10c6a422"
150
  },
151
  {
152
  "path": "scripts/train_math_ink_06_boundary_behavior_guard.py",
 
159
  "sha256": "28cfc7b9b695256cfa9199d912d1d938d402292b88e9411d42fddb3508446d3b"
160
  },
161
  {
162
+ "path": "scripts/sweep_math_ink_06_component_competition_guard.py",
163
+ "bytes": 9084,
164
+ "sha256": "e2dee616e2c6ebdc32281624889017685648b7076515242e04a1452d98d1c7da"
 
 
 
 
 
165
  },
166
  {
167
  "path": "contracts/aiflow_p_formula_v1.schema.json",
 
169
  "sha256": "c78c8a612d2f24a5c09fe1aa158b365481102869c3f84b3a5510a895b416c424"
170
  },
171
  {
172
+ "path": "colab/aiflow_math_ink_06_litert_bundle.zip",
173
+ "bytes": 17276380,
174
+ "sha256": "e20ed37a7c35daabaae72b6108204ae08da3b909a19f230528bb40ab198c7ca8"
175
  },
176
  {
177
+ "path": "colab/aiflow_math_ink_06_litert_bundle.manifest.json",
178
+ "bytes": 2978,
179
+ "sha256": "03a7ebfe56cc8855494ecfafb463290d4bbe9b0fb0582153e17b62ea6726bdd6"
180
  },
181
  {
182
+ "path": "exports/seed17/export_manifest.json",
183
+ "bytes": 1031,
184
+ "sha256": "06b79357ca9c80934925d8e7d59a81c3a9c1f4fdc017ca4a6ed19ff6580ed6ab"
185
  },
186
  {
187
  "path": "models/seed17/base_378.pt",
188
  "bytes": 8638290,
189
  "sha256": "f3b0e53b50e41cf57d4881c501371b35648cf049d603984a0edf6148c2b62bd7"
190
  },
191
+ {
192
+ "path": "exports/seed17/raster.pt2",
193
+ "bytes": 14380889,
194
+ "sha256": "f2b3f0612a7016f44bf9271399fd2ab522048bc301e75dbeb52c21b31356dfbd"
195
+ },
196
+ {
197
+ "path": "exports/seed17/online.pt2",
198
+ "bytes": 9921690,
199
+ "sha256": "fb07c249e49720310a0c627adfc1c21fa75f7203332e20b202622cbe05fa0214"
200
+ },
201
+ {
202
+ "path": "colab/AIFlow_Math_Ink_06_LiteRT.ipynb",
203
+ "bytes": 3505,
204
+ "sha256": "7c0a2118bc84576957048e882df33681eb9a4e2fe832011b4d73117c0b57b0c1"
205
+ },
206
  {
207
  "path": "pyproject.snapshot.toml",
208
  "bytes": 669,
 
215
  },
216
  {
217
  "path": "MODEL_INDEX.json",
218
+ "bytes": 2473,
219
+ "sha256": "8926674c956664b98d2fc1519e75355ad13efe57228d122ce208defebdfb90f1"
220
  },
221
  {
222
  "path": "README.md",
223
+ "bytes": 11411,
224
+ "sha256": "5e8ee6eef8985af8404540edc7fd000d5b03a095ed1eac05cabcfd8d346ab064"
225
  },
226
  {
227
  "path": "assets/stroke_encoding.svg",
 
238
  "bytes": 824191,
239
  "sha256": "18aff472c1bdb25a519b1553364229c9869aed1f36a9d663cf88a1bc808fab8f"
240
  },
 
 
 
 
 
241
  {
242
  "path": "reports/behavior_role_seed31.json",
243
  "bytes": 45412,
 
249
  "sha256": "e2072fec1b1065777f0e9218c48d27f096b22f5545e7cc8d9962939282719561"
250
  },
251
  {
252
+ "path": "reports/behavior_role_3seed_summary.json",
253
+ "bytes": 3567,
254
+ "sha256": "b16049218d2d006d952f59f55642fa9f3fbfbc3cc3a730fbd7b075f507c3b251"
255
  },
256
  {
257
+ "path": "reports/behavior_role_seed47.json",
258
+ "bytes": 30091,
259
+ "sha256": "3d4dcbe2f2b0afb483f992dacd62aab1089bdc41d55cc259bcb7fc21b5294db0"
260
  },
261
  {
262
  "path": "reports/local_baseline_guard_report.json",
 
269
  "sha256": "4d3db5f71e5008381dd072f2c10aafb22e08f96e23f2a32f82a3a46abd9c70b9"
270
  },
271
  {
272
+ "path": "reports/boundary_behavior_guard_report.json",
273
+ "bytes": 72053,
274
+ "sha256": "f55a230a9f0892b9676c71db46e9e527862b3d8da776dc43db767ce7a0f6a5c7"
275
  },
276
  {
277
+ "path": "models/seed47/online_adapter.pt",
278
+ "bytes": 2876038,
279
+ "sha256": "46394eeebad3d27d3178f78e693e160700ba3f454d79044714b835bc51c07056"
280
  },
281
  {
282
  "path": "models/seed31/base_378.pt",
 
284
  "sha256": "e3c57bc8cbbd4542cacf7ab9200a9031dcdc5ee63ffbd6ad6c9f7ef4f0a64f37"
285
  },
286
  {
287
+ "path": "models/seed17/online_adapter.pt",
288
+ "bytes": 2875654,
289
+ "sha256": "8ea224d7bc107a4c8b6cb91c9de6598000dae463d3e6d39480c081ea728cc87f"
290
  },
291
  {
292
+ "path": "models/seed17/behavior_role_head.pt",
293
+ "bytes": 75794,
294
+ "sha256": "57ced82a2600bd342bba68ba0636ecc3fc98c3dc461e26a132b5264d0a3d95bd"
295
  },
296
  {
297
+ "path": "models/seed31/behavior_role_head.pt",
298
+ "bytes": 75794,
299
+ "sha256": "b7c22cf49320b90a6fd103d39cc3ff08ea7734ab83275aad66e2b529b749d9ae"
300
  },
301
  {
302
  "path": "models/seed47/behavior_role_head.pt",
303
  "bytes": 75794,
304
  "sha256": "f28fad4e0289e3bc9400829f41aba9bbd75c43d445b7462a84aadbdbe635c10b"
305
+ },
306
+ {
307
+ "path": "models/seed47/base_378.pt",
308
+ "bytes": 8638290,
309
+ "sha256": "4131b21f6b00471c1c5a82aee4a2da45e599e42dc742fc1dd0dd8afb970f8939"
310
+ },
311
+ {
312
+ "path": "models/seed31/online_adapter.pt",
313
+ "bytes": 2875654,
314
+ "sha256": "fedbbab64cea6755e76e0633367ee45fbcc1d5726bb9dfdb7c5fe4bbc968bdf2"
315
  }
316
  ]
317
  }
MODEL_INDEX.json CHANGED
@@ -64,6 +64,8 @@
64
  "shared_state_applied": true,
65
  "top1_agreement": 1.0,
66
  "maximum_absolute_logit_error": 0.0,
 
 
67
  "litert_flatbuffer": false
68
  },
69
  "release_state": {
 
64
  "shared_state_applied": true,
65
  "top1_agreement": 1.0,
66
  "maximum_absolute_logit_error": 0.0,
67
+ "total_pt2_bytes": 24302579,
68
+ "representative_samples": 76,
69
  "litert_flatbuffer": false
70
  },
71
  "release_state": {
README.md CHANGED
@@ -191,7 +191,7 @@ Joint delta ์—†๋Š” main+shadow auxiliary device stress์—์„œ ์ตœ์•… exact/family
191
 
192
  ### Composite torch.export
193
 
194
- Export ๊ทธ๋ž˜ํ”„๋Š” ์ด์ œ base-only๊ฐ€ ์•„๋‹ˆ๋ผ online/raster modality adapter์™€ shared state๋ฅผ ํฌํ•จํ•œ๋‹ค. ์„ธ seed ๋ชจ๋‘ ๊ฐ ๊ฒฝ๋กœ ๋Œ€ํ‘œ ์ž…๋ ฅ 76๊ฐœ์—์„œ eager ๋Œ€๋น„ top-1 100% ์ผ์น˜, ์ตœ๋Œ€ logit ์ ˆ๋Œ€์˜ค์ฐจ 0.0์„ ๊ธฐ๋กํ–ˆ๋‹ค. Seed-17์˜ online/raster `.pt2` ํ•ฉ๊ณ„๋Š” 18,657,779 bytes๋‹ค.
195
 
196
  `.pt2`๋Š” Android์šฉ `.tflite`๊ฐ€ ์•„๋‹ˆ๋‹ค. LiteRT Torch 0.9.1 ๋ณ€ํ™˜๊ณผ Android runtime parity๋Š” ์•„์ง ์™„๋ฃŒ๋˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ `litert_exported=false`๋ฅผ ์œ ์ง€ํ•œ๋‹ค.
197
 
@@ -247,5 +247,6 @@ PyTorch checkpoint์™€ joblib/pickle์€ ์‹ ๋ขฐํ•  ์ˆ˜ ์—†๋Š” ์ถœ์ฒ˜์—์„œ ๋กœ๋“œ
247
  - P boundary ์ •์ • 3-seed: [`reports/p_boundary_joint_sharedfix_3seed_summary.json`](reports/p_boundary_joint_sharedfix_3seed_summary.json)
248
  - ์ •์ • main device stress: [`reports/p_boundary_device_stress_sharedfix_3seed.json`](reports/p_boundary_device_stress_sharedfix_3seed.json)
249
  - Seed-17 composite export: [`exports/seed17/export_manifest.json`](exports/seed17/export_manifest.json)
 
250
  - ์‹ค์ œ P formula schema: [`contracts/aiflow_p_formula_v1.schema.json`](contracts/aiflow_p_formula_v1.schema.json)
251
  - ํŒŒ์ผ checksum: [`MANIFEST.json`](MANIFEST.json)
 
191
 
192
  ### Composite torch.export
193
 
194
+ Export ๊ทธ๋ž˜ํ”„๋Š” ์ด์ œ base-only๊ฐ€ ์•„๋‹ˆ๋ผ online/raster modality adapter์™€ shared state๋ฅผ ํฌํ•จํ•œ๋‹ค. ์„ธ seed ๋ชจ๋‘ ๊ฐ ๊ฒฝ๋กœ ๋Œ€ํ‘œ ์ž…๋ ฅ 76๊ฐœ์—์„œ eager ๋Œ€๋น„ top-1 100% ์ผ์น˜, ์ตœ๋Œ€ logit ์ ˆ๋Œ€์˜ค์ฐจ 0.0์„ ๊ธฐ๋กํ–ˆ๋‹ค. Stale output ์žฌ์‚ฌ์šฉ์„ atomic replace๋กœ ์ˆ˜์ •ํ•œ fresh seed-17 online/raster `.pt2` ํ•ฉ๊ณ„๋Š” 24,302,579 bytes๋‹ค.
195
 
196
  `.pt2`๋Š” Android์šฉ `.tflite`๊ฐ€ ์•„๋‹ˆ๋‹ค. LiteRT Torch 0.9.1 ๋ณ€ํ™˜๊ณผ Android runtime parity๋Š” ์•„์ง ์™„๋ฃŒ๋˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ `litert_exported=false`๋ฅผ ์œ ์ง€ํ•œ๋‹ค.
197
 
 
247
  - P boundary ์ •์ • 3-seed: [`reports/p_boundary_joint_sharedfix_3seed_summary.json`](reports/p_boundary_joint_sharedfix_3seed_summary.json)
248
  - ์ •์ • main device stress: [`reports/p_boundary_device_stress_sharedfix_3seed.json`](reports/p_boundary_device_stress_sharedfix_3seed.json)
249
  - Seed-17 composite export: [`exports/seed17/export_manifest.json`](exports/seed17/export_manifest.json)
250
+ - LiteRT Colab notebook: [`colab/AIFlow_Math_Ink_06_LiteRT.ipynb`](colab/AIFlow_Math_Ink_06_LiteRT.ipynb)
251
  - ์‹ค์ œ P formula schema: [`contracts/aiflow_p_formula_v1.schema.json`](contracts/aiflow_p_formula_v1.schema.json)
252
  - ํŒŒ์ผ checksum: [`MANIFEST.json`](MANIFEST.json)
colab/AIFlow_Math_Ink_06_LiteRT.ipynb ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "nbformat": 4,
3
+ "nbformat_minor": 5,
4
+ "metadata": {
5
+ "colab": {"name": "AIFlow Math Ink 0.6 LiteRT"},
6
+ "kernelspec": {"display_name": "Python 3", "name": "python3"}
7
+ },
8
+ "cells": [
9
+ {
10
+ "cell_type": "markdown",
11
+ "metadata": {},
12
+ "source": [
13
+ "# AIFlow Math Ink 0.6 โ€” LiteRT ๋ณ€ํ™˜\n",
14
+ "\n",
15
+ "๊ณต๊ฐœ seed-17 composite ๋ชจ๋ธ์„ ๊ณต์‹ LiteRT Torch 0.9.1๋กœ ๋ณ€ํ™˜ํ•˜๊ณ  ์‹ค์ œ ๋Œ€ํ‘œ ์ž…๋ ฅ 76๊ฐœ์˜ PyTorch/LiteRT parity๋ฅผ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ถœ๋ ฅ gate๋ฅผ ํ†ต๊ณผํ•˜๊ธฐ ์ „์—๋Š” Android ๋ฐฐํฌ artifact๋กœ ์ทจ๊ธ‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."
16
+ ]
17
+ },
18
+ {
19
+ "cell_type": "code",
20
+ "execution_count": null,
21
+ "metadata": {},
22
+ "outputs": [],
23
+ "source": [
24
+ "!pip -q install litert-torch==0.9.1 pillow huggingface-hub\n"
25
+ ]
26
+ },
27
+ {
28
+ "cell_type": "code",
29
+ "execution_count": null,
30
+ "metadata": {},
31
+ "outputs": [],
32
+ "source": [
33
+ "from huggingface_hub import hf_hub_download\n",
34
+ "from pathlib import Path\n",
35
+ "from zipfile import ZipFile\n",
36
+ "import hashlib, json\n",
37
+ "\n",
38
+ "repo = 'cwLeeDev/aiflow-math-ink-06-intermediate'\n",
39
+ "archive = Path(hf_hub_download(repo_id=repo, filename='colab/aiflow_math_ink_06_litert_bundle.zip'))\n",
40
+ "root = Path('/content/aiflow_math_ink_06_litert')\n",
41
+ "root.mkdir(parents=True, exist_ok=True)\n",
42
+ "with ZipFile(archive) as bundle:\n",
43
+ " bundle.extractall(root)\n",
44
+ "manifest = json.loads((root / 'LITERT_COLAB_BUNDLE_MANIFEST.json').read_text(encoding='utf-8'))\n",
45
+ "for row in manifest['files']:\n",
46
+ " payload = (root / row['path']).read_bytes()\n",
47
+ " assert len(payload) == row['bytes']\n",
48
+ " assert hashlib.sha256(payload).hexdigest() == row['sha256']\n",
49
+ "print('bundle verified:', len(manifest['files']), 'files')\n"
50
+ ]
51
+ },
52
+ {
53
+ "cell_type": "code",
54
+ "execution_count": null,
55
+ "metadata": {},
56
+ "outputs": [],
57
+ "source": [
58
+ "%cd /content/aiflow_math_ink_06_litert\n",
59
+ "!PYTHONPATH=src python scripts/export_math_ink_06_litert.py \\\n",
60
+ " --checkpoint artifacts/base_378.pt \\\n",
61
+ " --adapter-checkpoint artifacts/online_adapter.pt \\\n",
62
+ " --representative-inputs artifacts/representative_inputs.pt \\\n",
63
+ " --convert-litert --output outputs\n"
64
+ ]
65
+ },
66
+ {
67
+ "cell_type": "code",
68
+ "execution_count": null,
69
+ "metadata": {},
70
+ "outputs": [],
71
+ "source": [
72
+ "report = json.loads((root / 'outputs/export_manifest.json').read_text(encoding='utf-8'))\n",
73
+ "assert report['torch_export_gate_passed']\n",
74
+ "assert report['litert']['online']['gate_passed']\n",
75
+ "assert report['litert']['raster']['gate_passed']\n",
76
+ "total_bytes = report['litert']['online']['bytes'] + report['litert']['raster']['bytes']\n",
77
+ "assert total_bytes <= 25 * 1024 * 1024\n",
78
+ "print('LiteRT gate passed, total bytes:', total_bytes)\n",
79
+ "report\n"
80
+ ]
81
+ },
82
+ {
83
+ "cell_type": "code",
84
+ "execution_count": null,
85
+ "metadata": {},
86
+ "outputs": [],
87
+ "source": [
88
+ "from google.colab import files\n",
89
+ "import shutil\n",
90
+ "shutil.make_archive('/content/aiflow_math_ink_06_litert_outputs', 'zip', root / 'outputs')\n",
91
+ "files.download('/content/aiflow_math_ink_06_litert_outputs.zip')\n"
92
+ ]
93
+ }
94
+ ]
95
+ }
colab/aiflow_math_ink_06_litert_bundle.manifest.json ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema": "aiflow-math-ink-06-litert-colab-bundle-v1",
3
+ "generated_at": "2026-07-23T20:13:15.384782+00:00",
4
+ "seed": 17,
5
+ "litert_torch_version": "0.9.1",
6
+ "representative_samples": 76,
7
+ "files": [
8
+ {
9
+ "path": "pyproject.toml",
10
+ "bytes": 669,
11
+ "sha256": "e9571da506b96d9ab6baa8f8e2c1d87cd8ee9fd0c880f3cf6646a99086ae3f09"
12
+ },
13
+ {
14
+ "path": "src/math_grid_drawer/__init__.py",
15
+ "bytes": 96,
16
+ "sha256": "16625f83572539172333f4146ceaf255f242b879010a9a2a44bc85cfb651396a"
17
+ },
18
+ {
19
+ "path": "src/math_grid_drawer/research/__init__.py",
20
+ "bytes": 91,
21
+ "sha256": "51bf186de1b9da930191d5d25d172c7e3372204784326c942b38f7af0775edf0"
22
+ },
23
+ {
24
+ "path": "src/math_grid_drawer/research/ink06_canonical.py",
25
+ "bytes": 14198,
26
+ "sha256": "3bd7337f222eec7355e5bdc6d485dc654549978aebbd5e0de87c19222373a17c"
27
+ },
28
+ {
29
+ "path": "src/math_grid_drawer/research/ink06_export.py",
30
+ "bytes": 4474,
31
+ "sha256": "37c67827f1c39df9cb0f6c45f782c58a2d1bdf5190e51070de817d1ba2693b67"
32
+ },
33
+ {
34
+ "path": "src/math_grid_drawer/research/math_ink_06.py",
35
+ "bytes": 54708,
36
+ "sha256": "6c41a7ff9958fe6bd396b3321586d7a935ef0206553ce633327962c2d27ffff4"
37
+ },
38
+ {
39
+ "path": "src/math_grid_drawer/research/raster_skeleton06.py",
40
+ "bytes": 6052,
41
+ "sha256": "d6807fc96c6e75e6bf12054382d29a227b1aa05fc867ab44badb6cca3fcb5cf2"
42
+ },
43
+ {
44
+ "path": "src/math_grid_drawer/research/skeleton_adapter06.py",
45
+ "bytes": 11697,
46
+ "sha256": "068cc8a616da25425827bda3aae4ae5d7a7619ec9623df77c6477447e5cf3d50"
47
+ },
48
+ {
49
+ "path": "src/math_grid_drawer/research/trajectory_sequence.py",
50
+ "bytes": 25290,
51
+ "sha256": "a3ad8193b6da8e9599c4291618983a40808a264075d2ecb3f205c711c3c5d032"
52
+ },
53
+ {
54
+ "path": "scripts/export_math_ink_06_litert.py",
55
+ "bytes": 11876,
56
+ "sha256": "3d5362341c29ef31493969ccf833c21cb1d4e02d8ccf9ccd217b038091f9e43c"
57
+ },
58
+ {
59
+ "path": "artifacts/base_378.pt",
60
+ "bytes": 8638290,
61
+ "sha256": "f3b0e53b50e41cf57d4881c501371b35648cf049d603984a0edf6148c2b62bd7"
62
+ },
63
+ {
64
+ "path": "artifacts/online_adapter.pt",
65
+ "bytes": 2875654,
66
+ "sha256": "8ea224d7bc107a4c8b6cb91c9de6598000dae463d3e6d39480c081ea728cc87f"
67
+ },
68
+ {
69
+ "path": "artifacts/representative_inputs.pt",
70
+ "bytes": 5721772,
71
+ "sha256": "8eda6e57a6fc1d32aa16b579b847bdd864538293bf37d8c1e226de7e5a21a2b6"
72
+ }
73
+ ],
74
+ "product_validation": false,
75
+ "bundle": "research\\runs\\math_ink_06_litert_colab_20260724\\aiflow_math_ink_06_litert_bundle.zip",
76
+ "bundle_bytes": 17276380,
77
+ "bundle_sha256": "e20ed37a7c35daabaae72b6108204ae08da3b909a19f230528bb40ab198c7ca8",
78
+ "verification": {
79
+ "schema": "aiflow-math-ink-06-litert-colab-bundle-v1",
80
+ "files": 13,
81
+ "failures": [],
82
+ "passed": true,
83
+ "product_validation": false
84
+ }
85
+ }
colab/aiflow_math_ink_06_litert_bundle.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e20ed37a7c35daabaae72b6108204ae08da3b909a19f230528bb40ab198c7ca8
3
+ size 17276380
exports/seed17/export_manifest.json CHANGED
@@ -12,7 +12,7 @@
12
  "max_absolute_logit_error": 0.0,
13
  "gate_passed": true,
14
  "path": "online.pt2",
15
- "bytes": 9192090
16
  },
17
  "raster": {
18
  "samples": 76,
@@ -21,7 +21,7 @@
21
  "max_absolute_logit_error": 0.0,
22
  "gate_passed": true,
23
  "path": "raster.pt2",
24
- "bytes": 9465689
25
  }
26
  },
27
  "litert_package_available": false,
 
12
  "max_absolute_logit_error": 0.0,
13
  "gate_passed": true,
14
  "path": "online.pt2",
15
+ "bytes": 9921690
16
  },
17
  "raster": {
18
  "samples": 76,
 
21
  "max_absolute_logit_error": 0.0,
22
  "gate_passed": true,
23
  "path": "raster.pt2",
24
+ "bytes": 14380889
25
  }
26
  },
27
  "litert_package_available": false,
exports/seed17/online.pt2 CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:d8a4fa6a631d5f1f4e8871b5c551d647530312260caace3cab4bca01004f7891
3
- size 9192090
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fb07c249e49720310a0c627adfc1c21fa75f7203332e20b202622cbe05fa0214
3
+ size 9921690
exports/seed17/raster.pt2 CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:419301d67bbfe7b807e1ea676a33827c749f632300392d54eaf48c701ca07512
3
- size 9465689
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f2b3f0612a7016f44bf9271399fd2ab522048bc301e75dbeb52c21b31356dfbd
3
+ size 14380889
reports/RESEARCH_REPORT.md CHANGED
@@ -407,7 +407,9 @@ Joint delta ์—†์ด ์ •์ •๋œ main๊ณผ ์ƒˆ auxiliary head๋งŒ ๋Œ€์ƒ์œผ๋กœ device s
407
 
408
  ๊ธฐ์กด export script๋„ base checkpoint๋งŒ ์ฝ์–ด ์‹ค์ œ main adapter๋ฅผ ์šฐํšŒํ–ˆ๋‹ค. Online์€ `online adapter โ†’ shared trajectory encoder`, raster๋Š” `virtual top-4 โ†’ raster adapter โ†’ shared trajectory encoder`๊ฐ€ ๊ทธ๋ž˜ํ”„ ์•ˆ์— ํฌํ•จ๋˜๋„๋ก ์ˆ˜์ •ํ–ˆ๋‹ค. Dual adapter๋Š” ์ž…๋ ฅ ๋ฐ์ดํ„ฐ์— ๋”ฐ๋ฅธ ๋Ÿฐํƒ€์ž„ ๋ถ„๊ธฐ ๋Œ€์‹  online/raster branch๋ฅผ export ์‹œ์ ์— ๊ณ ์ •ํ•œ๋‹ค.
409
 
410
- Seed 17ยท31ยท47 ๋ชจ๋‘ online/raster ๊ฐ๊ฐ ๋Œ€ํ‘œ ์ž…๋ ฅ 76๊ฐœ์—์„œ eager ๋Œ€๋น„ top-1 100% ์ผ์น˜, ์ตœ๋Œ€ logit ์ ˆ๋Œ€์˜ค์ฐจ 0.0์œผ๋กœ strict `torch.export` gate๋ฅผ ํ†ต๊ณผํ–ˆ๋‹ค. Seed๋‹น ๋‘ graph ํ•ฉ๊ณ„๋Š” 18,657,779 bytes๋กœ 25MB ์˜ˆ์‚ฐ ์•ˆ์ด๋‹ค. ๋‹ค๋งŒ `.pt2`๋Š” Android LiteRT artifact๊ฐ€ ์•„๋‹ˆ๋‹ค. ๊ณต์‹ LiteRT Torch 0.9.1์€ Linux ํ™˜๊ฒฝ์„ ์š”๊ตฌํ•˜๋ฉฐ WSL ๋ณ€ํ™˜ ํ™˜๊ฒฝ ์„ค์น˜ ์ค‘ WSL filesystem I/O ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ด `.tflite` ๋ณ€ํ™˜๊ณผ runtime parity๋Š” ์•„์ง ๋ฏธ์™„๋ฃŒ๋‹ค.
 
 
411
 
412
  ### ์‹ค์ œ P ์—ฐ์†์‹ ๋ฐ์ดํ„ฐ ๊ณ„์•ฝ
413
 
@@ -420,7 +422,7 @@ Seed 17ยท31ยท47 ๋ชจ๋‘ online/raster ๊ฐ๊ฐ ๋Œ€ํ‘œ ์ž…๋ ฅ 76๊ฐœ์—์„œ eager ๋Œ€
420
  - ์–‘์ˆ˜ canvas ํฌ๊ธฐ
421
  - token๊ณผ ์›๋ณธ raw stroke๊ฐ€ ํฌํ•จ๋œ ์ •๋‹ต symbol group
422
 
423
- Originยทwriterยทdeviceยทsource๊ฐ€ ๋‘˜ ์ด์ƒ์˜ split์— ๋‚˜ํƒ€๋‚˜๋ฉด ์ œํ’ˆ ํ‰๊ฐ€๋ฅผ ๊ฑฐ๋ถ€ํ•œ๋‹ค. Timestamp์™€ pressure๊ฐ€ ์—†๋Š” symbol์€ ์‚ญ์ œํ•˜๊ฑฐ๋‚˜ ๊ด€์ธก๊ฐ’์œผ๋กœ ์œ„์žฅํ•˜์ง€ ์•Š๊ณ  missing slice๋กœ ์„ผ๋‹ค. ๊ฒ€์ฆ๋œ formula์˜ ์‹ค์ œ symbol group์€ boundary ์Œ์„ฑ, ์ธ์ ‘ํ•œ ๋‘ symbol group ๊ฒฐํ•ฉ์€ boundary ์–‘์„ฑ์œผ๋กœ ๋งŒ๋“ ๋‹ค. ์ด ๊ณ„์•ฝ์€ ์‹ค์ œ ์„ฑ๋Šฅ๊ฐ’์„ ๋งŒ๋“ค์ง€๋Š” ์•Š์ง€๋งŒ, ํ–ฅํ›„ P ์ž…๋ ฅ์ด CROHME ์ •๋‹ต group์ด๋‚˜ ํ•ฉ์„ฑ ๊ณ ๋ฆฝ๊ธฐํ˜ธ proxy์™€ ์„ž์ด๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•œ๋‹ค. ์ „์ฒด ํšŒ๊ท€๋Š” 282๊ฐœ๊ฐ€ ํ†ต๊ณผํ–ˆ๋‹ค.
424
 
425
  ## ์‚ฐ์ถœ๋ฌผ
426
 
@@ -445,12 +447,15 @@ Originยทwriterยทdeviceยทsource๊ฐ€ ๋‘˜ ์ด์ƒ์˜ split์— ๋‚˜ํƒ€๋‚˜๋ฉด ์ œํ’ˆ ํ‰
445
  - `scripts/summarize_math_ink_06_p_boundary_joint.py`
446
  - `scripts/evaluate_math_ink_06_p_boundary_device_stress.py`
447
  - `scripts/export_math_ink_06_litert.py`
 
448
  - `scripts/preflight_math_ink_06_p_formula.py`
449
  - `scripts/analyze_crohme_lattice_failures.py`
450
  - `tests/test_behavior_context06.py`
451
  - `tests/test_behavior_role_head06.py`
452
  - `tests/test_p_formula_gate06.py`
453
  - `research/contracts/aiflow_p_formula_v1.schema.json`
 
 
454
  - `research/AIFlow-MATH-INK-0.6-BEHAVIOR-CONTEXT-REPORT-20260724.md`
455
  - `research/runs/math_ink_06_behavior_role_3seed_20260724/run_summary.json`
456
  - `research/runs/math_ink_06_behavior_grouping_audit_20260724/report.json`
 
407
 
408
  ๊ธฐ์กด export script๋„ base checkpoint๋งŒ ์ฝ์–ด ์‹ค์ œ main adapter๋ฅผ ์šฐํšŒํ–ˆ๋‹ค. Online์€ `online adapter โ†’ shared trajectory encoder`, raster๋Š” `virtual top-4 โ†’ raster adapter โ†’ shared trajectory encoder`๊ฐ€ ๊ทธ๋ž˜ํ”„ ์•ˆ์— ํฌํ•จ๋˜๋„๋ก ์ˆ˜์ •ํ–ˆ๋‹ค. Dual adapter๋Š” ์ž…๋ ฅ ๋ฐ์ดํ„ฐ์— ๋”ฐ๋ฅธ ๋Ÿฐํƒ€์ž„ ๋ถ„๊ธฐ ๋Œ€์‹  online/raster branch๋ฅผ export ์‹œ์ ์— ๊ณ ์ •ํ•œ๋‹ค.
409
 
410
+ Seed 17ยท31ยท47 ๋ชจ๋‘ online/raster ๊ฐ๊ฐ ๋Œ€ํ‘œ ์ž…๋ ฅ 76๊ฐœ์—์„œ eager ๋Œ€๋น„ top-1 100% ์ผ์น˜, ์ตœ๋Œ€ logit ์ ˆ๋Œ€์˜ค์ฐจ 0.0์œผ๋กœ strict `torch.export` gate๋ฅผ ํ†ต๊ณผํ–ˆ๋‹ค. ๊ธฐ์กด ์ถœ๋ ฅ ๊ฒฝ๋กœ๋ฅผ ์žฌ์‚ฌ์šฉํ•  ๋•Œ ์˜ค๋ž˜๋œ `.pt2`๊ฐ€ ๋‚จ๋Š” ๋ฌธ์ œ๋ฅผ ๋ฐœ๊ฒฌํ•ด `.part โ†’ atomic replace`๋กœ ์ˆ˜์ •ํ–ˆ๊ณ , fresh seed-17 ๋‘ graph ํ•ฉ๊ณ„๋Š” 24,302,579 bytes๋กœ 25MiB ์˜ˆ์‚ฐ ์•ˆ์ด๋‹ค. ๋‹ค๋งŒ `.pt2`๋Š” Android LiteRT artifact๊ฐ€ ์•„๋‹ˆ๋‹ค. ๊ณต์‹ LiteRT Torch 0.9.1์€ Linux ํ™˜๊ฒฝ์„ ์š”๊ตฌํ•˜๋ฉฐ WSL ๋ณ€ํ™˜ ํ™˜๊ฒฝ ์„ค์น˜ ์ค‘ WSL filesystem I/O ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ด `.tflite` ๋ณ€ํ™˜๊ณผ runtime parity๋Š” ์•„์ง ๋ฏธ์™„๋ฃŒ๋‹ค.
411
+
412
+ Linux/Colab ๋ณ€ํ™˜์„ ์œ„ํ•ด label์ด ์—†๋Š” ์‹ค์ œ HWRT-derived online/raster ๋Œ€ํ‘œ ์ž…๋ ฅ 76๊ฐœ๋ฅผ 5,721,772-byte cache๋กœ ๊ณ ์ •ํ–ˆ๋‹ค. ์ตœ์†Œ bundle์€ composite baseยทadapter, ํ•„์š”ํ•œ source 7๊ฐœ, converter์™€ ๋Œ€ํ‘œ cache๋งŒ ํฌํ•จํ•˜๋ฉฐ 13๊ฐœ entryยท17,276,380 bytes๋‹ค. Bundle ๋‚ด๋ถ€ SHA-256 ์ „์ฒด ๊ฒ€์ฆ๊ณผ ๋ณ„๋„ ๋””๋ ‰ํ„ฐ๋ฆฌ ์ถ”์ถœ ํ›„ composite export smoke๋ฅผ ํ†ต๊ณผํ–ˆ๋‹ค. Colab notebook์€ ๊ณต์‹ `litert-torch==0.9.1` ์„ค์น˜, ๊ณต๊ฐœ Hugging Face bundle ๋‹ค์šด๋กœ๋“œ, ํ•ด์‹œ ๊ฒ€์ฆ, online/raster `.tflite` ๋ณ€ํ™˜, 76๊ฐœ ์ „์ฒด top-1 100%ยทlogit ์˜ค์ฐจโ‰ค0.02ยทํ•ฉ๊ณ„โ‰ค25MiB gate๋ฅผ ์ž๋™ ์‹คํ–‰ํ•œ๋‹ค.
413
 
414
  ### ์‹ค์ œ P ์—ฐ์†์‹ ๋ฐ์ดํ„ฐ ๊ณ„์•ฝ
415
 
 
422
  - ์–‘์ˆ˜ canvas ํฌ๊ธฐ
423
  - token๊ณผ ์›๋ณธ raw stroke๊ฐ€ ํฌํ•จ๋œ ์ •๋‹ต symbol group
424
 
425
+ Originยทwriterยทdeviceยทsource๊ฐ€ ๋‘˜ ์ด์ƒ์˜ split์— ๋‚˜ํƒ€๋‚˜๋ฉด ์ œํ’ˆ ํ‰๊ฐ€๋ฅผ ๊ฑฐ๋ถ€ํ•œ๋‹ค. Timestamp์™€ pressure๊ฐ€ ์—†๋Š” symbol์€ ์‚ญ์ œํ•˜๊ฑฐ๋‚˜ ๊ด€์ธก๊ฐ’์œผ๋กœ ์œ„์žฅํ•˜์ง€ ์•Š๊ณ  missing slice๋กœ ์„ผ๋‹ค. ๊ฒ€์ฆ๋œ formula์˜ ์‹ค์ œ symbol group์€ boundary ์Œ์„ฑ, ์ธ์ ‘ํ•œ ๋‘ symbol group ๊ฒฐํ•ฉ์€ boundary ์–‘์„ฑ์œผ๋กœ ๋งŒ๋“ ๋‹ค. ์ด ๊ณ„์•ฝ์€ ์‹ค์ œ ์„ฑ๋Šฅ๊ฐ’์„ ๋งŒ๋“ค์ง€๋Š” ์•Š์ง€๋งŒ, ํ–ฅํ›„ P ์ž…๋ ฅ์ด CROHME ์ •๋‹ต group์ด๋‚˜ ํ•ฉ์„ฑ ๊ณ ๋ฆฝ๊ธฐํ˜ธ proxy์™€ ์„ž์ด๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•œ๋‹ค. ์ „์ฒด ํšŒ๊ท€๋Š” 284๊ฐœ๊ฐ€ ํ†ต๊ณผํ–ˆ๋‹ค.
426
 
427
  ## ์‚ฐ์ถœ๋ฌผ
428
 
 
447
  - `scripts/summarize_math_ink_06_p_boundary_joint.py`
448
  - `scripts/evaluate_math_ink_06_p_boundary_device_stress.py`
449
  - `scripts/export_math_ink_06_litert.py`
450
+ - `scripts/build_math_ink_06_litert_colab_bundle.py`
451
  - `scripts/preflight_math_ink_06_p_formula.py`
452
  - `scripts/analyze_crohme_lattice_failures.py`
453
  - `tests/test_behavior_context06.py`
454
  - `tests/test_behavior_role_head06.py`
455
  - `tests/test_p_formula_gate06.py`
456
  - `research/contracts/aiflow_p_formula_v1.schema.json`
457
+ - `research/colab/AIFlow_Math_Ink_06_LiteRT.ipynb`
458
+ - `research/runs/math_ink_06_litert_colab_20260724/aiflow_math_ink_06_litert_bundle.zip`
459
  - `research/AIFlow-MATH-INK-0.6-BEHAVIOR-CONTEXT-REPORT-20260724.md`
460
  - `research/runs/math_ink_06_behavior_role_3seed_20260724/run_summary.json`
461
  - `research/runs/math_ink_06_behavior_grouping_audit_20260724/report.json`
scripts/build_math_ink_06_litert_colab_bundle.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """AIFlow Math Ink 0.6 LiteRT ๋ณ€ํ™˜์— ํ•„์š”ํ•œ ์ตœ์†Œ Colab ZIP์„ ๊ฒฐ์ •์ ์œผ๋กœ ๋งŒ๋“ ๋‹ค."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ from datetime import datetime, timezone
7
+ from hashlib import sha256
8
+ import json
9
+ from pathlib import Path, PurePosixPath
10
+ from zipfile import ZIP_DEFLATED, ZIP_STORED, ZipFile, ZipInfo
11
+
12
+
13
+ PROJECT_ROOT = Path(__file__).parents[1]
14
+ MANIFEST_NAME_06 = "LITERT_COLAB_BUNDLE_MANIFEST.json"
15
+
16
+
17
+ def _sha256_bytes06(value: bytes) -> str:
18
+ """ํ•„์š” ๋ณ€์ˆ˜: file bytes. ์ž‘๋™ ์›๋ฆฌ: bundle entry์™€ manifest ๊ฒ€์ฆ์šฉ SHA-256์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค."""
19
+
20
+ return sha256(value).hexdigest()
21
+
22
+
23
+ def _bundle_files06(
24
+ base_checkpoint: Path, adapter_checkpoint: Path, representative_inputs: Path,
25
+ ) -> dict[str, Path]:
26
+ """ํ•„์š” ๋ณ€์ˆ˜: seed-17 composite artifact. ์ž‘๋™ ์›๋ฆฌ: converter๊ฐ€ importํ•˜๋Š” ๋‹ซํžŒ ์ตœ์†Œ ํŒŒ์ผ ์ง‘ํ•ฉ์„ ๋งคํ•‘ํ•œ๋‹ค."""
27
+
28
+ return {
29
+ "pyproject.toml": PROJECT_ROOT / "pyproject.toml",
30
+ "src/math_grid_drawer/__init__.py": PROJECT_ROOT / "research/colab/litert_bundle_package_init.py",
31
+ "src/math_grid_drawer/research/__init__.py": PROJECT_ROOT / "src/math_grid_drawer/research/__init__.py",
32
+ "src/math_grid_drawer/research/ink06_canonical.py": PROJECT_ROOT / "src/math_grid_drawer/research/ink06_canonical.py",
33
+ "src/math_grid_drawer/research/ink06_export.py": PROJECT_ROOT / "src/math_grid_drawer/research/ink06_export.py",
34
+ "src/math_grid_drawer/research/math_ink_06.py": PROJECT_ROOT / "src/math_grid_drawer/research/math_ink_06.py",
35
+ "src/math_grid_drawer/research/raster_skeleton06.py": PROJECT_ROOT / "src/math_grid_drawer/research/raster_skeleton06.py",
36
+ "src/math_grid_drawer/research/skeleton_adapter06.py": PROJECT_ROOT / "src/math_grid_drawer/research/skeleton_adapter06.py",
37
+ "src/math_grid_drawer/research/trajectory_sequence.py": PROJECT_ROOT / "src/math_grid_drawer/research/trajectory_sequence.py",
38
+ "scripts/export_math_ink_06_litert.py": PROJECT_ROOT / "scripts/export_math_ink_06_litert.py",
39
+ "artifacts/base_378.pt": base_checkpoint,
40
+ "artifacts/online_adapter.pt": adapter_checkpoint,
41
+ "artifacts/representative_inputs.pt": representative_inputs,
42
+ }
43
+
44
+
45
+ def build_litert_colab_bundle06(
46
+ base_checkpoint: Path,
47
+ adapter_checkpoint: Path,
48
+ representative_inputs: Path,
49
+ output: Path,
50
+ ) -> dict:
51
+ """ํ•„์š” ๋ณ€์ˆ˜: composite checkpointยท๋Œ€ํ‘œ ์ž…๋ ฅยทZIP ์ถœ๋ ฅ. ์ž‘๋™ ์›๋ฆฌ: ๊ฒฝ๋กœยทํฌ๊ธฐยทํ•ด์‹œ๊ฐ€ ๊ณ ์ •๋œ Linux ๋ณ€ํ™˜ bundle์„ ๋งŒ๋“ ๋‹ค."""
52
+
53
+ files = _bundle_files06(base_checkpoint, adapter_checkpoint, representative_inputs)
54
+ missing = [name for name, path in files.items() if not path.is_file()]
55
+ if missing:
56
+ raise FileNotFoundError(f"LiteRT bundle ํ•„์ˆ˜ ํŒŒ์ผ์ด ์—†์Šต๋‹ˆ๋‹ค: {missing}")
57
+ entries = []
58
+ payloads: dict[str, bytes] = {}
59
+ for name, path in files.items():
60
+ payload = path.read_bytes()
61
+ payloads[name] = payload
62
+ entries.append({
63
+ "path": name, "bytes": len(payload), "sha256": _sha256_bytes06(payload),
64
+ })
65
+ manifest = {
66
+ "schema": "aiflow-math-ink-06-litert-colab-bundle-v1",
67
+ "generated_at": datetime.now(timezone.utc).isoformat(),
68
+ "seed": 17,
69
+ "litert_torch_version": "0.9.1",
70
+ "representative_samples": 76,
71
+ "files": entries,
72
+ "product_validation": False,
73
+ }
74
+ output.parent.mkdir(parents=True, exist_ok=True)
75
+ temporary = output.with_suffix(output.suffix + ".part")
76
+ with ZipFile(temporary, "w", allowZip64=True) as bundle:
77
+ for name, payload in payloads.items():
78
+ compression = ZIP_STORED if PurePosixPath(name).suffix in {".pt"} else ZIP_DEFLATED
79
+ info = ZipInfo(name, date_time=(1980, 1, 1, 0, 0, 0))
80
+ info.compress_type = compression
81
+ info.external_attr = 0o644 << 16
82
+ bundle.writestr(info, payload)
83
+ info = ZipInfo(MANIFEST_NAME_06, date_time=(1980, 1, 1, 0, 0, 0))
84
+ info.compress_type = ZIP_DEFLATED
85
+ info.external_attr = 0o644 << 16
86
+ bundle.writestr(
87
+ info, json.dumps(manifest, ensure_ascii=False, indent=2).encode("utf-8") + b"\n",
88
+ )
89
+ temporary.replace(output)
90
+ manifest["bundle"] = str(output)
91
+ manifest["bundle_bytes"] = output.stat().st_size
92
+ manifest["bundle_sha256"] = sha256(output.read_bytes()).hexdigest()
93
+ return manifest
94
+
95
+
96
+ def verify_litert_colab_bundle06(bundle_path: Path) -> dict:
97
+ """ํ•„์š” ๋ณ€์ˆ˜: ์ƒ์„ฑ ZIP. ์ž‘๋™ ์›๋ฆฌ: ์ถ”์ถœ ์—†์ด ๋ชจ๋“  entry์˜ ์•ˆ์ „ ๊ฒฝ๋กœยทํฌ๊ธฐยทSHA-256์„ ์žฌ๊ฒ€์ฆํ•œ๋‹ค."""
98
+
99
+ failures = []
100
+ with ZipFile(bundle_path) as bundle:
101
+ manifest = json.loads(bundle.read(MANIFEST_NAME_06).decode("utf-8"))
102
+ names = set(bundle.namelist())
103
+ for row in manifest["files"]:
104
+ name = str(row["path"])
105
+ path = PurePosixPath(name)
106
+ if path.is_absolute() or ".." in path.parts:
107
+ failures.append({"path": name, "reason": "unsafe_path"})
108
+ elif name not in names:
109
+ failures.append({"path": name, "reason": "missing"})
110
+ else:
111
+ payload = bundle.read(name)
112
+ if len(payload) != int(row["bytes"]):
113
+ failures.append({"path": name, "reason": "bytes"})
114
+ elif _sha256_bytes06(payload) != str(row["sha256"]):
115
+ failures.append({"path": name, "reason": "sha256"})
116
+ return {
117
+ "schema": manifest["schema"], "files": len(manifest["files"]),
118
+ "failures": failures, "passed": not failures, "product_validation": False,
119
+ }
120
+
121
+
122
+ def main() -> None:
123
+ """ํ•„์š” ๋ณ€์ˆ˜: CLI artifact ๊ฒฝ๋กœ. ์ž‘๋™ ์›๋ฆฌ: bundle ์ƒ์„ฑ ์งํ›„ archive ์ž์ฒด ๊ฒ€์ฆ๊นŒ์ง€ ์ˆ˜ํ–‰ํ•œ๋‹ค."""
124
+
125
+ parser = argparse.ArgumentParser(description="Build Math Ink 0.6 LiteRT Colab bundle")
126
+ parser.add_argument("--base-checkpoint", type=Path, required=True)
127
+ parser.add_argument("--adapter-checkpoint", type=Path, required=True)
128
+ parser.add_argument("--representative-inputs", type=Path, required=True)
129
+ parser.add_argument("--output", type=Path, required=True)
130
+ args = parser.parse_args()
131
+ report = build_litert_colab_bundle06(
132
+ args.base_checkpoint, args.adapter_checkpoint, args.representative_inputs, args.output,
133
+ )
134
+ report["verification"] = verify_litert_colab_bundle06(args.output)
135
+ if not report["verification"]["passed"]:
136
+ raise ValueError(f"LiteRT Colab bundle ๊ฒ€์ฆ ์‹คํŒจ: {report['verification']['failures']}")
137
+ report_path = args.output.with_suffix(args.output.suffix + ".manifest.json")
138
+ report_path.write_text(
139
+ json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8",
140
+ )
141
+ print(json.dumps(report, ensure_ascii=False, indent=2))
142
+
143
+
144
+ if __name__ == "__main__":
145
+ main()
scripts/export_math_ink_06_litert.py CHANGED
@@ -16,7 +16,6 @@ SOURCE_ROOT = PROJECT_ROOT / "src"
16
  if str(SOURCE_ROOT) not in sys.path:
17
  sys.path.insert(0, str(SOURCE_ROOT))
18
 
19
- from math_grid_drawer.research.external_corpus import read_jsonl
20
  from math_grid_drawer.research.ink06_canonical import canonicalize_ink06, render_canonical_ink
21
  from math_grid_drawer.research.ink06_export import (
22
  OnlineExportWrapper06, RasterExportWrapper06, exported_equivalence06,
@@ -31,6 +30,8 @@ from math_grid_drawer.research.math_ink_06 import VirtualTrajectoryAdapter06
31
  def _representative_inputs(baseline_report: Path, data_path: Path) -> tuple[list[tuple[torch.Tensor, ...]], list[tuple[torch.Tensor, ...]]]:
32
  """ํ•„์š” ๋ณ€์ˆ˜: strict baselineยทHWRT JSONL. ์ž‘๋™ ์›๋ฆฌ: ๊ณ ์ • 76๊ฐœ๋ฅผ 128ร—19์™€ 128ร—128 ๋Œ€ํ‘œ ์ž…๋ ฅ์œผ๋กœ ์žฌ๊ตฌ์„ฑํ•œ๋‹ค."""
33
 
 
 
34
  baseline = json.loads(baseline_report.read_text(encoding="utf-8"))
35
  accepted_ids = {row["sample_id"] for row in baseline["rows"] if row["raster_gate"]}
36
  records = {row["sample_id"]: row for row in read_jsonl(data_path) if row["sample_id"] in accepted_ids}
@@ -47,26 +48,77 @@ def _representative_inputs(baseline_report: Path, data_path: Path) -> tuple[list
47
  return online_inputs, raster_inputs
48
 
49
 
50
- def _convert_litert(wrapper: torch.nn.Module, sample: tuple[torch.Tensor, ...], output: Path) -> dict:
51
- """ํ•„์š” ๋ณ€์ˆ˜: export ํ˜ธํ™˜ wrapperยทํ‘œ๋ณธยท์ถœ๋ ฅ. ์ž‘๋™ ์›๋ฆฌ: ๊ณต์‹ litert_torch.convert API๋กœ flatbuffer๋ฅผ ๋งŒ๋“ค๊ณ  1ํ‘œ๋ณธ ์˜ค์ฐจ๋ฅผ ๊ฒ€์‚ฌํ•œ๋‹ค."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
  import litert_torch # type: ignore[import-not-found]
54
 
55
- edge_model = litert_torch.convert(wrapper.eval(), sample)
56
- eager = wrapper(*sample)
57
- edge = edge_model(*sample)
58
- eager_values = eager if isinstance(eager, tuple) else (eager,)
59
- edge_values = edge if isinstance(edge, tuple) else (edge,)
60
- max_error = max(
61
- float(np.max(np.abs(left.detach().cpu().numpy() - np.asarray(right))))
62
- for left, right in zip(eager_values, edge_values)
63
- )
64
- top1_equal = int(np.argmax(eager_values[0].detach().cpu().numpy())) == int(np.argmax(np.asarray(edge_values[0])))
 
 
 
 
 
 
 
 
 
65
  edge_model.export(str(output))
66
  return {
67
  "converted": True, "path": output.name, "bytes": output.stat().st_size,
68
- "sample_top1_agreement": top1_equal, "sample_max_absolute_logit_error": max_error,
69
- "sample_gate_passed": top1_equal and max_error <= 0.02,
 
 
70
  }
71
 
72
 
@@ -103,12 +155,24 @@ def _adapter_branches06(adapter: torch.nn.Module) -> tuple[torch.nn.Module, torc
103
  return adapter, adapter
104
 
105
 
 
 
 
 
 
 
 
 
 
 
106
  def main() -> None:
107
  """ํ•„์š” ๋ณ€์ˆ˜: checkpointยทstrict ์ž…๋ ฅยท์ถœ๋ ฅยท๋ณ€ํ™˜ ์„ ํƒ. ์ž‘๋™ ์›๋ฆฌ: ๋‘ ๊ฒฝ๋กœ์˜ export/๋™๋“ฑ์„ฑ/์„ ํƒ์  LiteRT ๊ฒฐ๊ณผ๋ฅผ manifest๋กœ ๊ณ ์ •ํ•œ๋‹ค."""
108
 
109
  parser = argparse.ArgumentParser(description="Export Math Ink 0.6 for LiteRT")
110
  parser.add_argument("--checkpoint", type=Path, required=True)
111
  parser.add_argument("--adapter-checkpoint", type=Path, required=True)
 
 
112
  parser.add_argument("--baseline-report", type=Path, default=PROJECT_ROOT / "research/runs/full_model_stage2_20260722/isolated_checkpoint.json")
113
  parser.add_argument("--data", type=Path, default=PROJECT_ROOT / "research/data/open_pretrain/hwrt_expanded_v2/hwrt_expanded.jsonl.gz")
114
  parser.add_argument("--output", type=Path, required=True)
@@ -124,13 +188,21 @@ def main() -> None:
124
  engine.model, adapter=raster_adapter,
125
  fusion_mode=str(fusion["mode"]), score_weight=float(fusion["score_weight"]),
126
  ).eval()
127
- online_inputs, raster_inputs = _representative_inputs(args.baseline_report, args.data)
 
 
 
 
 
 
 
 
128
  online_export = torch.export.export(online, online_inputs[0], strict=True)
129
  raster_export = torch.export.export(raster, raster_inputs[0], strict=True)
130
  args.output.mkdir(parents=True, exist_ok=True)
131
  online_path, raster_path = args.output / "online.pt2", args.output / "raster.pt2"
132
- torch.export.save(online_export, online_path)
133
- torch.export.save(raster_export, raster_path)
134
  report = {
135
  "checkpoint": str(args.checkpoint), "adapter_checkpoint": str(args.adapter_checkpoint),
136
  "adapter_architecture": str(adapter_payload["adapter_architecture"]),
@@ -149,8 +221,8 @@ def main() -> None:
149
  report["litert"] = {"converted": False, "reason": "litert_torch_not_installed"}
150
  else:
151
  report["litert"] = {
152
- "online": _convert_litert(online, online_inputs[0], args.output / "online.tflite"),
153
- "raster": _convert_litert(raster, raster_inputs[0], args.output / "raster.tflite"),
154
  }
155
  report["torch_export_gate_passed"] = all(
156
  bool(report["torch_export"][name]["gate_passed"]) for name in ("online", "raster")
 
16
  if str(SOURCE_ROOT) not in sys.path:
17
  sys.path.insert(0, str(SOURCE_ROOT))
18
 
 
19
  from math_grid_drawer.research.ink06_canonical import canonicalize_ink06, render_canonical_ink
20
  from math_grid_drawer.research.ink06_export import (
21
  OnlineExportWrapper06, RasterExportWrapper06, exported_equivalence06,
 
30
  def _representative_inputs(baseline_report: Path, data_path: Path) -> tuple[list[tuple[torch.Tensor, ...]], list[tuple[torch.Tensor, ...]]]:
31
  """ํ•„์š” ๋ณ€์ˆ˜: strict baselineยทHWRT JSONL. ์ž‘๋™ ์›๋ฆฌ: ๊ณ ์ • 76๊ฐœ๋ฅผ 128ร—19์™€ 128ร—128 ๋Œ€ํ‘œ ์ž…๋ ฅ์œผ๋กœ ์žฌ๊ตฌ์„ฑํ•œ๋‹ค."""
32
 
33
+ from math_grid_drawer.research.external_corpus import read_jsonl
34
+
35
  baseline = json.loads(baseline_report.read_text(encoding="utf-8"))
36
  accepted_ids = {row["sample_id"] for row in baseline["rows"] if row["raster_gate"]}
37
  records = {row["sample_id"]: row for row in read_jsonl(data_path) if row["sample_id"] in accepted_ids}
 
48
  return online_inputs, raster_inputs
49
 
50
 
51
+ def _load_representative_inputs06(
52
+ cache_path: Path,
53
+ ) -> tuple[list[tuple[torch.Tensor, ...]], list[tuple[torch.Tensor, ...]]]:
54
+ """ํ•„์š” ๋ณ€์ˆ˜: ๊ณ ์ • representative cache. ์ž‘๋™ ์›๋ฆฌ: Colab์—์„œ๋„ ๊ฐ™์€ 76๊ฐœ batch-1 ์ž…๋ ฅ์„ ๋ณต์›ํ•œ๋‹ค."""
55
+
56
+ payload = torch.load(cache_path, map_location="cpu", weights_only=True)
57
+ if payload.get("schema") != "aiflow-math-ink-06-export-inputs-v1":
58
+ raise ValueError("์ง€์›ํ•˜์ง€ ์•Š๋Š” export representative cache์ž…๋‹ˆ๋‹ค.")
59
+ online, raster = payload["online"], payload["raster"]
60
+ if online.ndim != 3 or online.shape[1:] != (128, 19):
61
+ raise ValueError(f"online representative shape๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค: {tuple(online.shape)}")
62
+ if raster.ndim != 4 or raster.shape[1:] != (1, 128, 128):
63
+ raise ValueError(f"raster representative shape๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค: {tuple(raster.shape)}")
64
+ if len(online) != len(raster) or not len(online):
65
+ raise ValueError("online/raster representative ๋ถ„๋ชจ๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.")
66
+ return (
67
+ [(value.unsqueeze(0),) for value in online],
68
+ [(value.unsqueeze(0),) for value in raster],
69
+ )
70
+
71
+
72
+ def _save_representative_inputs06(
73
+ output: Path,
74
+ online_inputs: list[tuple[torch.Tensor, ...]],
75
+ raster_inputs: list[tuple[torch.Tensor, ...]],
76
+ ) -> None:
77
+ """ํ•„์š” ๋ณ€์ˆ˜: ๋‘ ๋Œ€ํ‘œ ์ž…๋ ฅ ๋ชฉ๋กยท์ถœ๋ ฅ. ์ž‘๋™ ์›๋ฆฌ: ์‹ค์ œ HWRT-derived ์ž…๋ ฅ๋งŒ tensor cache๋กœ ๊ณ ์ •ํ•œ๋‹ค."""
78
+
79
+ torch.save({
80
+ "schema": "aiflow-math-ink-06-export-inputs-v1",
81
+ "online": torch.cat([row[0] for row in online_inputs], dim=0).cpu(),
82
+ "raster": torch.cat([row[0] for row in raster_inputs], dim=0).cpu(),
83
+ "samples": len(online_inputs),
84
+ "contains_labels": False,
85
+ "product_validation": False,
86
+ }, output)
87
+
88
+
89
+ def _convert_litert(
90
+ wrapper: torch.nn.Module, samples: list[tuple[torch.Tensor, ...]], output: Path,
91
+ ) -> dict:
92
+ """ํ•„์š” ๋ณ€์ˆ˜: export ํ˜ธํ™˜ wrapperยท๋Œ€ํ‘œ ์ž…๋ ฅ ์ „์ฒดยท์ถœ๋ ฅ. ์ž‘๋™ ์›๋ฆฌ: ๊ณต์‹ converter ๋’ค 76๊ฐœ top-1/logit parity๋ฅผ ๊ฒ€์‚ฌํ•œ๋‹ค."""
93
 
94
  import litert_torch # type: ignore[import-not-found]
95
 
96
+ edge_model = litert_torch.convert(wrapper.eval(), samples[0])
97
+ top1_matches = 0
98
+ max_error = 0.0
99
+ with torch.inference_mode():
100
+ for sample in samples:
101
+ eager = wrapper(*sample)
102
+ edge = edge_model(*sample)
103
+ eager_values = eager if isinstance(eager, tuple) else (eager,)
104
+ edge_values = edge if isinstance(edge, tuple) else (edge,)
105
+ if len(eager_values) != len(edge_values):
106
+ raise ValueError("PyTorch/LiteRT ์ถœ๋ ฅ ๊ฐœ์ˆ˜๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.")
107
+ max_error = max(max_error, max(
108
+ float(np.max(np.abs(left.detach().cpu().numpy() - np.asarray(right))))
109
+ for left, right in zip(eager_values, edge_values)
110
+ ))
111
+ top1_matches += int(
112
+ np.argmax(eager_values[0].detach().cpu().numpy(), axis=-1)[0]
113
+ == np.argmax(np.asarray(edge_values[0]), axis=-1)[0]
114
+ )
115
  edge_model.export(str(output))
116
  return {
117
  "converted": True, "path": output.name, "bytes": output.stat().st_size,
118
+ "samples": len(samples), "top1_matches": top1_matches,
119
+ "top1_agreement": top1_matches / max(len(samples), 1),
120
+ "max_absolute_logit_error": max_error,
121
+ "gate_passed": top1_matches == len(samples) and max_error <= 0.02,
122
  }
123
 
124
 
 
155
  return adapter, adapter
156
 
157
 
158
+ def _save_exported_program06(exported: torch.export.ExportedProgram, output: Path) -> None:
159
+ """ํ•„์š” ๋ณ€์ˆ˜: export programยท๋ชฉํ‘œ ํŒŒ์ผ. ์ž‘๋™ ์›๋ฆฌ: stale ZIP ์žฌ์‚ฌ์šฉ ์—†์ด ์ž„์‹œ ํŒŒ์ผ์„ ์›์ž์ ์œผ๋กœ ๊ต์ฒดํ•œ๋‹ค."""
160
+
161
+ temporary = output.with_suffix(output.suffix + ".part")
162
+ if temporary.exists():
163
+ temporary.unlink()
164
+ torch.export.save(exported, temporary)
165
+ temporary.replace(output)
166
+
167
+
168
  def main() -> None:
169
  """ํ•„์š” ๋ณ€์ˆ˜: checkpointยทstrict ์ž…๋ ฅยท์ถœ๋ ฅยท๋ณ€ํ™˜ ์„ ํƒ. ์ž‘๋™ ์›๋ฆฌ: ๋‘ ๊ฒฝ๋กœ์˜ export/๋™๋“ฑ์„ฑ/์„ ํƒ์  LiteRT ๊ฒฐ๊ณผ๋ฅผ manifest๋กœ ๊ณ ์ •ํ•œ๋‹ค."""
170
 
171
  parser = argparse.ArgumentParser(description="Export Math Ink 0.6 for LiteRT")
172
  parser.add_argument("--checkpoint", type=Path, required=True)
173
  parser.add_argument("--adapter-checkpoint", type=Path, required=True)
174
+ parser.add_argument("--representative-inputs", type=Path)
175
+ parser.add_argument("--save-representative-inputs", type=Path)
176
  parser.add_argument("--baseline-report", type=Path, default=PROJECT_ROOT / "research/runs/full_model_stage2_20260722/isolated_checkpoint.json")
177
  parser.add_argument("--data", type=Path, default=PROJECT_ROOT / "research/data/open_pretrain/hwrt_expanded_v2/hwrt_expanded.jsonl.gz")
178
  parser.add_argument("--output", type=Path, required=True)
 
188
  engine.model, adapter=raster_adapter,
189
  fusion_mode=str(fusion["mode"]), score_weight=float(fusion["score_weight"]),
190
  ).eval()
191
+ if args.representative_inputs:
192
+ online_inputs, raster_inputs = _load_representative_inputs06(args.representative_inputs)
193
+ else:
194
+ online_inputs, raster_inputs = _representative_inputs(args.baseline_report, args.data)
195
+ if args.save_representative_inputs:
196
+ args.save_representative_inputs.parent.mkdir(parents=True, exist_ok=True)
197
+ _save_representative_inputs06(
198
+ args.save_representative_inputs, online_inputs, raster_inputs,
199
+ )
200
  online_export = torch.export.export(online, online_inputs[0], strict=True)
201
  raster_export = torch.export.export(raster, raster_inputs[0], strict=True)
202
  args.output.mkdir(parents=True, exist_ok=True)
203
  online_path, raster_path = args.output / "online.pt2", args.output / "raster.pt2"
204
+ _save_exported_program06(online_export, online_path)
205
+ _save_exported_program06(raster_export, raster_path)
206
  report = {
207
  "checkpoint": str(args.checkpoint), "adapter_checkpoint": str(args.adapter_checkpoint),
208
  "adapter_architecture": str(adapter_payload["adapter_architecture"]),
 
221
  report["litert"] = {"converted": False, "reason": "litert_torch_not_installed"}
222
  else:
223
  report["litert"] = {
224
+ "online": _convert_litert(online, online_inputs, args.output / "online.tflite"),
225
+ "raster": _convert_litert(raster, raster_inputs, args.output / "raster.tflite"),
226
  }
227
  report["torch_export_gate_passed"] = all(
228
  bool(report["torch_export"][name]["gate_passed"]) for name in ("online", "raster")