itstheraj commited on
Commit
309d916
·
0 Parent(s):

initial commit

Browse files
.gitattributes ADDED
@@ -0,0 +1 @@
 
 
1
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ .ruff_cache/
2
+ .venv/
3
+ __pycache__/
4
+ *.swp
5
+ *.kate-swp
6
+ *~
LICENSE ADDED
@@ -0,0 +1,474 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Lowdown Labs Lovely License 1.0 (LLLL-1.0)
2
+
3
+ Everything in this repository (the model weights, the configuration, and the code) is
4
+ released by Lowdown Labs under two licenses that apply at the same time. To use this work
5
+ you must comply with BOTH of them. Where a term in one is stricter than the other, the
6
+ stricter term controls. "Lowdown Labs Lovely License 1.0" is a convenience name for this
7
+ exact pair; it is not a new legal instrument.
8
+
9
+ SPDX-License-Identifier: CC-BY-NC-4.0 AND LicenseRef-Hippocratic-3.0
10
+
11
+ Commercial licensing. The grant below is non-commercial only. Commercial licenses are
12
+ sold separately by Lowdown Labs on a per-customer basis. To use this work, its weights, or
13
+ its outputs for any commercial purpose, contact Lowdown Labs to purchase a commercial
14
+ license. A commercial license does not remove the Hippocratic ethical-use obligations in
15
+ Part 2; those apply to commercial licensees as well.
16
+
17
+ ==============================================================================
18
+ Part 1 of 2. Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0)
19
+ ==============================================================================
20
+
21
+ You may share and adapt this work for non-commercial purposes, with attribution to
22
+ Lowdown Labs. Commercial use is not granted under this license.
23
+
24
+ Full legal text: https://creativecommons.org/licenses/by-nc/4.0/legalcode
25
+ Plain-language summary: https://creativecommons.org/licenses/by-nc/4.0/
26
+ SPDX-License-Identifier: CC-BY-NC-4.0
27
+
28
+ ==============================================================================
29
+ Part 2 of 2. The Hippocratic License 3.0 (ethical use)
30
+ ==============================================================================
31
+
32
+ Module set enabled: bds, cl, eco, extr, ffd, law, media, mil, my, soc, sup, sv, usta.
33
+ Canonical build: https://firstdonoharm.dev/build/?modules=bds,cl,eco,extr,ffd,law,media,mil,my,soc,sup,sv,usta
34
+
35
+ The verbatim official Hippocratic License 3.0 text for exactly this module set follows,
36
+ between the markers.
37
+
38
+ --------------------- BEGIN OFFICIAL HIPPOCRATIC LICENSE 3.0 TEXT ---------------------
39
+
40
+ HIPPOCRATIC LICENSE
41
+
42
+ Version 3.0, October 2021
43
+
44
+ https://firstdonoharm.dev/version/3/0/bds-cl-eco-extr-ffd-law-media-mil-my-soc-sup-sv-usta.txt
45
+
46
+ TERMS AND CONDITIONS
47
+
48
+ TERMS AND CONDITIONS FOR USE, COPY, MODIFICATION, PREPARATION OF DERIVATIVE
49
+ WORK, REPRODUCTION, AND DISTRIBUTION:
50
+
51
+ 1. DEFINITIONS:
52
+
53
+ This section defines certain terms used throughout this license agreement.
54
+
55
+ 1.1. “License” means the terms and conditions, as stated herein, for use, copy,
56
+ modification, preparation of derivative work, reproduction, and distribution of
57
+ Software (as defined below).
58
+
59
+ 1.2. “Licensor” means the copyright and/or patent owner or entity authorized by
60
+ the copyright and/or patent owner that is granting the License.
61
+
62
+ 1.3. “Licensee” means the individual or entity exercising permissions granted by
63
+ this License, including the use, copy, modification, preparation of derivative
64
+ work, reproduction, and distribution of Software (as defined below).
65
+
66
+ 1.4. “Software” means any copyrighted work, including but not limited to
67
+ software code, authored by Licensor and made available under this License.
68
+
69
+ 1.5. “Supply Chain” means the sequence of processes involved in the production
70
+ and/or distribution of a commodity, good, or service offered by the Licensee.
71
+
72
+ 1.6. “Supply Chain Impacted Party” or “Supply Chain Impacted Parties” means any
73
+ person(s) directly impacted by any of Licensee’s Supply Chain, including the
74
+ practices of all persons or entities within the Supply Chain prior to a good or
75
+ service reaching the Licensee.
76
+
77
+ 1.7. “Duty of Care” is defined by its use in tort law, delict law, and/or
78
+ similar bodies of law closely related to tort and/or delict law, including
79
+ without limitation, a requirement to act with the watchfulness, attention,
80
+ caution, and prudence that a reasonable person in the same or similar
81
+ circumstances would use towards any Supply Chain Impacted Party.
82
+
83
+ 1.8. “Worker” is defined to include any and all permanent, temporary, and agency
84
+ workers, as well as piece-rate, salaried, hourly paid, legal young (minors),
85
+ part-time, night, and migrant workers.
86
+
87
+ 2. INTELLECTUAL PROPERTY GRANTS:
88
+
89
+ This section identifies intellectual property rights granted to a Licensee.
90
+
91
+ 2.1. Grant of Copyright License: Subject to the terms and conditions of this
92
+ License, Licensor hereby grants to Licensee a worldwide, non-exclusive,
93
+ no-charge, royalty-free copyright license to use, copy, modify, prepare
94
+ derivative work, reproduce, or distribute the Software, Licensor authored
95
+ modified software, or other work derived from the Software.
96
+
97
+ 2.2. Grant of Patent License: Subject to the terms and conditions of this
98
+ License, Licensor hereby grants Licensee a worldwide, non-exclusive, no-charge,
99
+ royalty-free patent license to make, have made, use, offer to sell, sell,
100
+ import, and otherwise transfer Software.
101
+
102
+ 3. ETHICAL STANDARDS:
103
+
104
+ This section lists conditions the Licensee must comply with in order to have
105
+ rights under this License.
106
+
107
+ The rights granted to the Licensee by this License are expressly made subject to
108
+ the Licensee’s ongoing compliance with the following conditions:
109
+
110
+ * 3.1. The Licensee SHALL NOT, whether directly or indirectly, through agents
111
+ or assigns:
112
+
113
+ * 3.1.1. Infringe upon any person’s right to life or security of person,
114
+ engage in extrajudicial killings, or commit murder, without lawful cause
115
+ (See Article 3, United Nations Universal Declaration of Human Rights;
116
+ Article 6, International Covenant on Civil and Political Rights)
117
+
118
+ * 3.1.2. Hold any person in slavery, servitude, or forced labor (See Article
119
+ 4, United Nations Universal Declaration of Human Rights; Article 8,
120
+ International Covenant on Civil and Political Rights);
121
+
122
+ * 3.1.3. Contribute to the institution of slavery, slave trading, forced
123
+ labor, or unlawful child labor (See Article 4, United Nations Universal
124
+ Declaration of Human Rights; Article 8, International Covenant on Civil and
125
+ Political Rights);
126
+
127
+ * 3.1.4. Torture or subject any person to cruel, inhumane, or degrading
128
+ treatment or punishment (See Article 5, United Nations Universal
129
+ Declaration of Human Rights; Article 7, International Covenant on Civil and
130
+ Political Rights);
131
+
132
+ * 3.1.5. Discriminate on the basis of sex, gender, sexual orientation, race,
133
+ ethnicity, nationality, religion, caste, age, medical disability or
134
+ impairment, and/or any other like circumstances (See Article 7, United
135
+ Nations Universal Declaration of Human Rights; Article 2, International
136
+ Covenant on Economic, Social and Cultural Rights; Article 26, International
137
+ Covenant on Civil and Political Rights);
138
+
139
+ * 3.1.6. Prevent any person from exercising his/her/their right to seek an
140
+ effective remedy by a competent court or national tribunal (including
141
+ domestic judicial systems, international courts, arbitration bodies, and
142
+ other adjudicating bodies) for actions violating the fundamental rights
143
+ granted to him/her/them by applicable constitutions, applicable laws, or by
144
+ this License (See Article 8, United Nations Universal Declaration of Human
145
+ Rights; Articles 9 and 14, International Covenant on Civil and Political
146
+ Rights);
147
+
148
+ * 3.1.7. Subject any person to arbitrary arrest, detention, or exile (See
149
+ Article 9, United Nations Universal Declaration of Human Rights; Article 9,
150
+ International Covenant on Civil and Political Rights);
151
+
152
+ * 3.1.8. Subject any person to arbitrary interference with a person’s
153
+ privacy, family, home, or correspondence without the express written
154
+ consent of the person (See Article 12, United Nations Universal Declaration
155
+ of Human Rights; Article 17, International Covenant on Civil and Political
156
+ Rights);
157
+
158
+ * 3.1.9. Arbitrarily deprive any person of his/her/their property (See
159
+ Article 17, United Nations Universal Declaration of Human Rights);
160
+
161
+ * 3.1.10. Forcibly remove indigenous peoples from their lands or territories
162
+ or take any action with the aim or effect of dispossessing indigenous
163
+ peoples from their lands, territories, or resources, including without
164
+ limitation the intellectual property or traditional knowledge of indigenous
165
+ peoples, without the free, prior, and informed consent of indigenous
166
+ peoples concerned (See Articles 8 and 10, United Nations Declaration on the
167
+ Rights of Indigenous Peoples);
168
+ * 3.1.11. Fossil Fuel Divestment: Be an individual or entity, or a
169
+ representative, agent, affiliate, successor, attorney, or assign of an
170
+ individual or entity, on the FFI Solutions Carbon Underground 200 list
171
+ [https://www.ffisolutions.com/research-analytics-index-solutions/research-screening/the-carbon-underground-200/?cn-reloaded=1];
172
+
173
+ * 3.1.12. Ecocide: Commit ecocide:
174
+
175
+ * 3.1.12.1. For the purpose of this section, “ecocide” means unlawful or
176
+ wanton acts committed with knowledge that there is a substantial
177
+ likelihood of severe and either widespread or long-term damage to the
178
+ environment being caused by those acts;
179
+
180
+ * 3.1.12.2. For the purpose of further defining ecocide and the terms
181
+ contained in the previous paragraph:
182
+
183
+ * 3.1.12.2.1. “Wanton” means with reckless disregard for damage which
184
+ would be clearly excessive in relation to the social and economic
185
+ benefits anticipated;
186
+
187
+ * 3.1.12.2.2. “Severe” means damage which involves very serious adverse
188
+ changes, disruption, or harm to any element of the environment,
189
+ including grave impacts on human life or natural, cultural, or
190
+ economic resources;
191
+
192
+ * 3.1.12.2.3. “Widespread” means damage which extends beyond a limited
193
+ geographic area, crosses state boundaries, or is suffered by an entire
194
+ ecosystem or species or a large number of human beings;
195
+
196
+ * 3.1.12.2.4. “Long-term” means damage which is irreversible or which
197
+ cannot be redressed through natural recovery within a reasonable
198
+ period of time; and
199
+
200
+ * 3.1.12.2.5. “Environment” means the earth, its biosphere, cryosphere,
201
+ lithosphere, hydrosphere, and atmosphere, as well as outer space
202
+
203
+ (See Section II, Independent Expert Panel for the Legal Definition of
204
+ Ecocide, Stop Ecocide Foundation and the Promise Institute for Human
205
+ Rights at UCLA School of Law, June 2021);
206
+
207
+ * 3.1.13. Extractive Industries: Be an individual or entity, or a
208
+ representative, agent, affiliate, successor, attorney, or assign of an
209
+ individual or entity, that engages in fossil fuel or mineral exploration,
210
+ extraction, development, or sale;
211
+
212
+ * 3.1.14. Boycott / Divestment / Sanctions: Be an individual or entity, or a
213
+ representative, agent, affiliate, successor, attorney, or assign of an
214
+ individual or entity, identified by the Boycott, Divestment, Sanctions
215
+ (“BDS”) movement on its website (https://bdsmovement.net/
216
+ [https://bdsmovement.net/] and
217
+ https://bdsmovement.net/get-involved/what-to-boycott
218
+ [https://bdsmovement.net/get-involved/what-to-boycott]) as a target for
219
+ boycott;
220
+
221
+ * 3.1.15. Myanmar: Be an individual or entity that:
222
+
223
+ * 3.1.15.1. engages in any commercial transactions with the
224
+ Myanmar/Burmese military junta; or
225
+
226
+ * 3.1.15.2. is a representative, agent, affiliate, successor, attorney, or
227
+ assign of the Myanmar/Burmese government;
228
+
229
+ * 3.1.16. US Tariff Act: Be an individual or entity:
230
+
231
+ * 3.1.16.1. which U.S. Customs and Border Protection (CBP) has currently
232
+ issued a Withhold Release Order (WRO) or finding against based on
233
+ reasonable suspicion of forced labor; or
234
+
235
+ * 3.1.16.2. that is a representative, agent, affiliate, successor,
236
+ attorney, or assign of an individual or entity that does business with
237
+ an individual or entity which currently has a WRO or finding from CBP
238
+ issued against it based on reasonable suspicion of forced labor;
239
+
240
+ * 3.1.17. Mass Surveillance: Be a government agency or multinational
241
+ corporation, or a representative, agent, affiliate, successor, attorney,
242
+ or assign of a government or multinational corporation, which participates
243
+ in mass surveillance programs;
244
+
245
+ * 3.1.18. Military Activities: Be an entity or a representative, agent,
246
+ affiliate, successor, attorney, or assign of an entity which conducts
247
+ military activities;
248
+
249
+ * 3.1.19. Law Enforcement: Be an individual or entity, or a representative,
250
+ agent, affiliate, successor, attorney, or assign of an individual or
251
+ entity, that provides good or services to, or otherwise enters into any
252
+ commercial contracts with, any local, state, or federal law enforcement
253
+ agency;
254
+
255
+ * 3.1.20. Media: Be an individual or entity, or a representative, agent,
256
+ affiliate, successor, attorney, or assign of an individual or entity, that
257
+ broadcasts messages promoting killing, torture, or other forms of extreme
258
+ violence;
259
+
260
+ * 3.1.21. Interfere with Workers’ free exercise of the right to organize and
261
+ associate (See Article 20, United Nations Universal Declaration of Human
262
+ Rights; C087 - Freedom of Association and Protection of the Right to
263
+ Organise Convention, 1948 (No. 87), International Labour Organization;
264
+ Article 8, International Covenant on Economic, Social and Cultural Rights);
265
+ and
266
+
267
+ * 3.1.22. Harm the environment in a manner inconsistent with local, state,
268
+ national, or international law.
269
+
270
+ * 3.2. The Licensee SHALL:
271
+
272
+ * 3.2.1. Social Auditing: Only use social auditing mechanisms that adhere to
273
+ Worker-Driven Social Responsibility Network’s Statement of Principles
274
+ (https://wsr-network.org/what-is-wsr/statement-of-principles/
275
+ [https://wsr-network.org/what-is-wsr/statement-of-principles/]) over
276
+ traditional social auditing mechanisms, to the extent the Licensee uses
277
+ any social auditing mechanisms at all;
278
+
279
+ * 3.2.2. Supply Chain: Provide clear, accessible supply chain data to the
280
+ public in accordance with the following conditions:
281
+
282
+ * 3.2.2.1. All data will be on Licensee’s website and/or, to the extent
283
+ Licensee is a representative, agent, affiliate, successor, attorney,
284
+ subsidiary, or assign, on Licensee’s principal’s or parent’s website or
285
+ some other online platform accessible to the public via an internet
286
+ search on a common internet search engine; and
287
+
288
+ * 3.2.2.2. Data published will include, where applicable, manufacturers,
289
+ top tier suppliers, subcontractors, cooperatives, component parts
290
+ producers, and farms;
291
+
292
+ * 3.2.3. Provide equal pay for equal work where the performance of such work
293
+ requires equal skill, effort, and responsibility, and which are performed
294
+ under similar working conditions, except where such payment is made
295
+ pursuant to:
296
+
297
+ * 3.2.3.1. A seniority system;
298
+
299
+ * 3.2.3.2. A merit system;
300
+
301
+ * 3.2.3.3. A system which measures earnings by quantity or quality of
302
+ production; or
303
+
304
+ * 3.2.3.4. A differential based on any other factor other than sex, gender,
305
+ sexual orientation, race, ethnicity, nationality, religion, caste, age,
306
+ medical disability or impairment, and/or any other like circumstances
307
+ (See 29 U.S.C.A. § 206(d)(1); Article 23, United Nations Universal
308
+ Declaration of Human Rights; Article 7, International Covenant on
309
+ Economic, Social and Cultural Rights; Article 26, International Covenant
310
+ on Civil and Political Rights); and
311
+
312
+ * 3.2.4. Allow for reasonable limitation of working hours and periodic
313
+ holidays with pay (See Article 24, United Nations Universal Declaration of
314
+ Human Rights; Article 7, International Covenant on Economic, Social and
315
+ Cultural Rights).
316
+
317
+ 4. SUPPLY CHAIN IMPACTED PARTIES:
318
+
319
+ This section identifies additional individuals or entities that a Licensee could
320
+ harm as a result of violating the Ethical Standards section, the condition that
321
+ the Licensee must voluntarily accept a Duty of Care for those individuals or
322
+ entities, and the right to a private right of action that those individuals or
323
+ entities possess as a result of violations of the Ethical Standards section.
324
+
325
+ 4.1. In addition to the above Ethical Standards, Licensee voluntarily accepts a
326
+ Duty of Care for Supply Chain Impacted Parties of this License, including
327
+ individuals and communities impacted by violations of the Ethical Standards. The
328
+ Duty of Care is breached when a provision within the Ethical Standards section
329
+ is violated by a Licensee, one of its successors or assigns, or by an individual
330
+ or entity that exists within the Supply Chain prior to a good or service
331
+ reaching the Licensee.
332
+
333
+ 4.2. Breaches of the Duty of Care, as stated within this section, shall create a
334
+ private right of action, allowing any Supply Chain Impacted Party harmed by the
335
+ Licensee to take legal action against the Licensee in accordance with applicable
336
+ negligence laws, whether they be in tort law, delict law, and/or similar bodies
337
+ of law closely related to tort and/or delict law, regardless if Licensee is
338
+ directly responsible for the harms suffered by a Supply Chain Impacted Party.
339
+ Nothing in this section shall be interpreted to include acts committed by
340
+ individuals outside of the scope of his/her/their employment.
341
+
342
+ 5. NOTICE: This section explains when a Licensee must notify others of the
343
+ License.
344
+
345
+ 5.1. Distribution of Notice: Licensee must ensure that everyone who receives a
346
+ copy of or uses any part of Software from Licensee, with or without changes,
347
+ also receives the License and the copyright notice included with Software (and
348
+ if included by the Licensor, patent, trademark, and attribution notice).
349
+ Licensee must ensure that License is prominently displayed so that any
350
+ individual or entity seeking to download, copy, use, or otherwise receive any
351
+ part of Software from Licensee is notified of this License and its terms and
352
+ conditions. Licensee must cause any modified versions of the Software to carry
353
+ prominent notices stating that Licensee changed the Software.
354
+
355
+ 5.2. Modified Software: Licensee is free to create modifications of the Software
356
+ and distribute only the modified portion created by Licensee, however, any
357
+ derivative work stemming from the Software or its code must be distributed
358
+ pursuant to this License, including this Notice provision.
359
+
360
+ 5.3. Recipients as Licensees: Any individual or entity that uses, copies,
361
+ modifies, reproduces, distributes, or prepares derivative work based upon the
362
+ Software, all or part of the Software’s code, or a derivative work developed by
363
+ using the Software, including a portion of its code, is a Licensee as defined
364
+ above and is subject to the terms and conditions of this License.
365
+
366
+ 6. REPRESENTATIONS AND WARRANTIES:
367
+
368
+ 6.1. Disclaimer of Warranty: TO THE FULL EXTENT ALLOWED BY LAW, THIS SOFTWARE
369
+ COMES “AS IS,” WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED, AND LICENSOR SHALL NOT
370
+ BE LIABLE TO ANY PERSON OR ENTITY FOR ANY DAMAGES OR OTHER LIABILITY ARISING
371
+ FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THIS LICENSE, UNDER ANY
372
+ LEGAL CLAIM.
373
+
374
+ 6.2. Limitation of Liability: LICENSEE SHALL HOLD LICENSOR HARMLESS AGAINST ANY
375
+ AND ALL CLAIMS, DEBTS, DUES, LIABILITIES, LIENS, CAUSES OF ACTION, DEMANDS,
376
+ OBLIGATIONS, DISPUTES, DAMAGES, LOSSES, EXPENSES, ATTORNEYS’ FEES, COSTS,
377
+ LIABILITIES, AND ALL OTHER CLAIMS OF EVERY KIND AND NATURE WHATSOEVER, WHETHER
378
+ KNOWN OR UNKNOWN, ANTICIPATED OR UNANTICIPATED, FORESEEN OR UNFORESEEN, ACCRUED
379
+ OR UNACCRUED, DISCLOSED OR UNDISCLOSED, ARISING OUT OF OR RELATING TO LICENSEE’S
380
+ USE OF THE SOFTWARE. NOTHING IN THIS SECTION SHOULD BE INTERPRETED TO REQUIRE
381
+ LICENSEE TO INDEMNIFY LICENSOR, NOR REQUIRE LICENSOR TO INDEMNIFY LICENSEE.
382
+
383
+ 7. TERMINATION
384
+
385
+ 7.1. Violations of Ethical Standards or Breaching Duty of Care: If Licensee
386
+ violates the Ethical Standards section or Licensee, or any other person or
387
+ entity within the Supply Chain prior to a good or service reaching the Licensee,
388
+ breaches its Duty of Care to Supply Chain Impacted Parties, Licensee must remedy
389
+ the violation or harm caused by Licensee within 30 days of being notified of the
390
+ violation or harm. If Licensee fails to remedy the violation or harm within 30
391
+ days, all rights in the Software granted to Licensee by License will be null and
392
+ void as between Licensor and Licensee.
393
+
394
+ 7.2. Failure of Notice: If any person or entity notifies Licensee in writing
395
+ that Licensee has not complied with the Notice section of this License, Licensee
396
+ can keep this License by taking all practical steps to comply within 30 days
397
+ after the notice of noncompliance. If Licensee does not do so, Licensee’s
398
+ License (and all rights licensed hereunder) will end immediately.
399
+
400
+ 7.3. Judicial Findings: In the event Licensee is found by a civil, criminal,
401
+ administrative, or other court of competent jurisdiction, or some other
402
+ adjudicating body with legal authority, to have committed actions which are in
403
+ violation of the Ethical Standards or Supply Chain Impacted Party sections of
404
+ this License, all rights granted to Licensee by this License will terminate
405
+ immediately.
406
+
407
+ 7.4. Patent Litigation: If Licensee institutes patent litigation against any
408
+ entity (including a cross-claim or counterclaim in a suit) alleging that the
409
+ Software, all or part of the Software’s code, or a derivative work developed
410
+ using the Software, including a portion of its code, constitutes direct or
411
+ contributory patent infringement, then any patent license, along with all other
412
+ rights, granted to Licensee under this License will terminate as of the date
413
+ such litigation is filed.
414
+
415
+ 7.5. Additional Remedies: Termination of the License by failing to remedy harms
416
+ in no way prevents Licensor or Supply Chain Impacted Party from seeking
417
+ appropriate remedies at law or in equity.
418
+
419
+ 8. MISCELLANEOUS:
420
+
421
+ 8.1. Conditions: Sections 3, 4.1, 5.1, 5.2, 7.1, 7.2, 7.3, and 7.4 are
422
+ conditions of the rights granted to Licensee in the License.
423
+
424
+ 8.2. Equitable Relief: Licensor and any Supply Chain Impacted Party shall be
425
+ entitled to equitable relief, including injunctive relief or specific
426
+ performance of the terms hereof, in addition to any other remedy to which they
427
+ are entitled at law or in equity.
428
+
429
+ 8.3. Copyleft: Modified software, source code, or other derivative work must be
430
+ licensed, in its entirety, under the exact same conditions as this License.
431
+
432
+ 8.4. Severability: If any term or provision of this License is determined to be
433
+ invalid, illegal, or unenforceable by a court of competent jurisdiction, any
434
+ such determination of invalidity, illegality, or unenforceability shall not
435
+ affect any other term or provision of this License or invalidate or render
436
+ unenforceable such term or provision in any other jurisdiction. If the
437
+ determination of invalidity, illegality, or unenforceability by a court of
438
+ competent jurisdiction pertains to the terms or provisions contained in the
439
+ Ethical Standards section of this License, all rights in the Software granted to
440
+ Licensee shall be deemed null and void as between Licensor and Licensee.
441
+
442
+ 8.5. Section Titles: Section titles are solely written for organizational
443
+ purposes and should not be used to interpret the language within each section.
444
+
445
+ 8.6. Citations: Citations are solely written to provide context for the source
446
+ of the provisions in the Ethical Standards.
447
+
448
+ 8.7. Section Summaries: Some sections have a brief italicized description which
449
+ is provided for the sole purpose of briefly describing the section and should
450
+ not be used to interpret the terms of the License.
451
+
452
+ 8.8. Entire License: This is the entire License between the Licensor and
453
+ Licensee with respect to the claims released herein and that the consideration
454
+ stated herein is the only consideration or compensation to be paid or exchanged
455
+ between them for this License. This License cannot be modified or amended except
456
+ in a writing signed by Licensor and Licensee.
457
+
458
+ 8.9. Successors and Assigns: This License shall be binding upon and inure to the
459
+ benefit of the Licensor’s and Licensee’s respective heirs, successors, and
460
+ assigns.
461
+ ---------------------- END OFFICIAL HIPPOCRATIC LICENSE 3.0 TEXT ----------------------
462
+
463
+ ==============================================================================
464
+ Attribution, commercial use, and warranty
465
+ ==============================================================================
466
+
467
+ Attribution: cite this work as described in the model card (README.md), section
468
+ "How to cite". Attribution to Lowdown Labs is required under CC BY-NC 4.0.
469
+
470
+ Commercial use: CC BY-NC 4.0 does not grant commercial rights. Commercial licenses are
471
+ sold by Lowdown Labs; contact Lowdown Labs to purchase one.
472
+
473
+ No warranty: this work is provided as is, without warranty of any kind. See the model card
474
+ for the intended use, the evaluated conditions, and the known limitations.
README.md ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: other
3
+ license_name: lowdown-labs-lovely-license-1.0
4
+ license_link: LICENSE
5
+ tags:
6
+ - fela
7
+ - fourier-neural-operator
8
+ - fno
9
+ - gated-deltanet
10
+ - cpu
11
+ - on-device
12
+ - code
13
+ - autocomplete
14
+ - fill-in-the-middle
15
+ - constant-memory
16
+ library_name: transformers
17
+ pipeline_tag: text-generation
18
+ language:
19
+ - code
20
+ model-index:
21
+ - name: fela-autocomplete
22
+ results:
23
+ - task:
24
+ type: text-generation
25
+ name: Fill in the middle single line infilling
26
+ dataset:
27
+ type: humaneval-singlelineinfilling
28
+ name: HumanEval SingleLineInfilling
29
+ metrics:
30
+ - type: pass@1
31
+ value: 54.6
32
+ name: FIM pass@1
33
+ ---
34
+
35
+ # FELA LLM 1.5: On device code autocomplete (fill in the middle)
36
+
37
+ FELA Autocomplete is a code completion model. You give it the code before your cursor, and
38
+ optionally more codebase context, and it writes the piece that goes in between. This is
39
+ the same fill in the middle idea that powers the gray autocomplete text in a modern editor.
40
+ It is a 1.79 billion parameter model that runs on a plain CPU with no GPU, so it can sit
41
+ inside a local IDE helper or an on premises code tool and complete code without sending your
42
+ source to the cloud.
43
+
44
+ It was warm started from Qwen2.5-Coder 1.5B and clean room distilled, then annealed and
45
+ supervised fine tuned for fill in the middle. Our key niche insight is to provide for inline single line
46
+ completion, not full text generation. We feel this a great compromise to have a speedy
47
+ development experience while still having a product that doesn't contribute to cognitive
48
+ atrophy and feels better to use.
49
+
50
+ # Headline result: fill in the middle
51
+
52
+ We wanted to apply the FELA methodology as an LLM. Given a tight training budget while we
53
+ study how to scale this new architecture, we thought a smaller model would be a great
54
+ autocomplete tool to demonstrate the power of efficient computing.
55
+
56
+ We measure functional pass@1 with the HumanEval-SingleLineInfilling methodology, so
57
+ a completion counts only when it makes the code run correctly, not when it matches the
58
+ original character for character.
59
+
60
+ | Stage | FIM Pass@1 | FIM EM | FIM Edit Sim | MBPP | HumanEval |
61
+ |---|---|---|---|---|---|
62
+ | Base (15B token distill) | 49.6% | 42.0 | 73.6 | 13.0 | 11.0 |
63
+ | Plus fill in the middle anneal | 54.4% | 47.4 | 77.6 | 16.0 | 9.8 |
64
+ | Plus fill in the middle SFT (final) | **54.6%** | 46.8 | **78.4** | **18.0** | 11.0 |
65
+
66
+ Over half of single line completions functionally work. We lead with FIM pass@1 because that
67
+ is the product metric. The Rust server auto formats the output in microseconds, so we optimize
68
+ for semantic correctness (pass@1 and edit similarity), not whitespace exact match.
69
+
70
+ MBPP and HumanEval are shown for context only. They ask the model to write a whole function
71
+ from a description, which is the wrong lens for an autocomplete model and low by design at this
72
+ size. We provide it to compare with other language models at size ~1.5B. Do not read the HumanEval number as the quality of the product; read the FIM pass@1.
73
+
74
+ # How it got here
75
+
76
+ 1. Base: clean room distill from Qwen2.5-Coder 1.5B to 15 billion tokens, on a permissive code
77
+ mix with fill in the middle in the objective.
78
+ 2. Fill in the middle anneal (the big lever): a further billion tokens with a line aware fill
79
+ in the middle objective (mask whole lines, matching how the eval scores lines) across
80
+ Python, JavaScript, TypeScript, Java, Go, Rust, and C plus plus. This train and eval
81
+ alignment gave the largest jump.
82
+ 3. Fill in the middle SFT: a further 1.2 billion tokens with the native Qwen fill in the middle
83
+ tokens and the loss on the middle span. This consolidated the gains and lifted general code
84
+ (MBPP 16 to 18).
85
+
86
+ # What goes in, what comes out
87
+
88
+ - Input: your code as text, split into a prefix (before the cursor) and an optional suffix
89
+ (after the cursor). The text is turned into tokens with the Qwen2.5-Coder tokenizer, which
90
+ carries the fill in the middle markers `<|fim_prefix|>`, `<|fim_suffix|>`, and
91
+ `<|fim_middle|>`.
92
+ - Output: the code that belongs between the prefix and the suffix. By default it writes one
93
+ line and stops, which is what an inline autocomplete wants. With no suffix it simply
94
+ continues the prefix.
95
+ - In plain terms: you type `import numpy as ` and it finishes the line with ` np`; you give it
96
+ the body of `def add(a, b):` with the call `add(2, 3)` waiting below it, and it fills in
97
+ ` return a + b`.
98
+
99
+ # Why we built it this way
100
+
101
+ The model has no growing cache of past tokens, which is the usual reason memory creeps up as
102
+ context gets longer. Its sequence mixers are Fourier Neural Operators (a filter the model
103
+ learns and applies in the frequency domain) for global context, Gated-DeltaNet recall layers
104
+ that overwrite an old memory when a new one arrives (so keys do not interfere), and a Landmark
105
+ routing layer that lets a token look back at summaries of earlier chunks. None of these keep a
106
+ per token attention cache, so the working memory stays small and fixed no matter how much code
107
+ is in the window. That is what lets it decode on a low power CPU next to you, with the source
108
+ never leaving the machine.
109
+
110
+ The full stack is 28 layers at width 1536, in the repeating pattern of three Fourier mixers
111
+ then one Gated-DeltaNet recall layer, with a Landmark routing layer every seventh layer.
112
+
113
+ # Performance
114
+
115
+ The model runs on CPU with no GPU. Two paths run these weights:
116
+
117
+ | Path | Format | Size on disk | Single core decode |
118
+ |---|---|---|---|
119
+ | Reference (this repo, `modeling.py`) | bf16 | 3.58 GB | about 4 tokens per second |
120
+ | Production (FELA server, separate Rust runtime) | int8 | about 2.08 GB | about 22 tokens per second |
121
+
122
+ The `modeling.py` loader here is the plain, readable reference path in pure PyTorch. It loads
123
+ the weights, runs a real forward, and decodes on CPU, which is enough for the quickstart and for
124
+ verifying the model. It is a correctness path, not the fast path, so do not read its speed as the
125
+ product speed. Loading `model_int8.safetensors` in Python is smaller in memory but not faster,
126
+ because the pure torch int8 path dequantizes the weights on the fly.
127
+
128
+ The fast path is the CPU native FELA server, a separate Rust runtime that runs these weights as
129
+ int8 with a constant state streaming decode and no KV cache. On one core it decodes at about 22
130
+ tokens per second, roughly 20 times the plain f32 path, measured at this model's shape with a
131
+ steady state O(1) decode step. Single stream decode is bound by memory bandwidth, since it
132
+ streams the full int8 weight set once per token, so adding cores does not speed up one stream
133
+ (measured flat from one to eight cores); extra cores serve more streams at once and speed up the
134
+ prefill. The server's Rust int8 forward is checked against the PyTorch model with a golden test,
135
+ so the fast serving path produces the same model, not a lookalike.
136
+
137
+ # What to expect
138
+
139
+ Some real completions from the final model:
140
+
141
+ - `import numpy as ` gives ` np`
142
+ - prefix `def add(a, b):` with the call `add(2, 3)` in the suffix gives ` return a + b`
143
+ - prefix `def is_even(n):\n return n % 2 == ` gives `0`
144
+
145
+ Where it earns its keep is single line fill in the middle on the patterns developers write all
146
+ day - and it can be made more useful with prefilled repository context.
147
+ It is a 1.5B class model, so it is not a whole function generator or a reasoning model:
148
+ multi line blocks, novel algorithms, and anything that needs deep reasoning about your specific
149
+ code are outside what it is built for. Read completions before you use them.
150
+
151
+ # How to run it
152
+
153
+ It runs on CPU; no GPU is required. See `quickstart/` for a runnable example. The short
154
+ version, using the self contained `modeling.py`, `model_cpu_gpt2.py`, and `config.json` in
155
+ this repo:
156
+
157
+ ```python
158
+ from modeling import load_model
159
+ m = load_model("/path/to/this/repo")
160
+
161
+ # Fill in the middle: code before and after the cursor -> the middle
162
+ r = m.complete("def add(a, b):\n ", suffix="\nresult = add(2, 3)\n")
163
+ print(r["middle"]) # e.g. " return a + b"
164
+
165
+ # Plain autocomplete: continue a single line
166
+ print(m.complete("import numpy as ")["middle"]) # e.g. " np"
167
+
168
+ # Load the smaller int8 weights instead (same completions, smaller on disk)
169
+ m8 = load_model("/path/to/this/repo", quant="int8")
170
+ ```
171
+
172
+ It also loads with transformers directly. The code is custom, so pass `trust_remote_code=True`:
173
+
174
+ ```python
175
+ import torch
176
+ from transformers import AutoModelForCausalLM, AutoTokenizer
177
+
178
+ model = AutoModelForCausalLM.from_pretrained("lowdown-labs/fela-autocomplete", trust_remote_code=True, dtype=torch.bfloat16)
179
+ tok = AutoTokenizer.from_pretrained("lowdown-labs/fela-autocomplete", trust_remote_code=True)
180
+ prompt = "<|fim_prefix|>def add(a, b):\n <|fim_suffix|>\nresult = add(2, 3)\n<|fim_middle|>"
181
+ enc = tok(prompt, return_tensors="pt")
182
+ out = model.generate(**enc, max_new_tokens=8, use_cache=False)
183
+ print(tok.decode(out[0, enc["input_ids"].shape[1]:], skip_special_tokens=True))
184
+ ```
185
+
186
+ The loader reads the bf16 `model.safetensors` by default. Pass `quant="int8"` to load the
187
+ smaller `model_int8.safetensors` instead; it dequantizes the linear weights with their per
188
+ channel scales and keeps the Fourier filters in bf16, giving the same completions. Pass
189
+ `quant="auto"` to prefer bf16 when present and fall back to int8.
190
+
191
+ `complete` is greedy by default, so a skeptic gets the same real output twice. For an
192
+ interactive playground, see the Hugging Face Space in `space/`. For production serving, use
193
+ the CPU native FELA server (`https://github.com/Lowdown-Labs/fela_server`).
194
+
195
+ ## Loading, exports, and verification
196
+
197
+ - Weights ship as `model.safetensors` in bf16 (no pickle code execution risk). `config.json`
198
+ holds the architecture hyperparameters. `tokenizer.json` is the Qwen2.5-Coder tokenizer with
199
+ the fill in the middle tokens. The architecture is defined in the self contained
200
+ `model_cpu_gpt2.py`, and the pure torch CPU paths for the Gated-DeltaNet and Landmark layers
201
+ are in `cpu_delta.py`, `cpu_landmark.py`, `cpu_swa.py`, and `cpu_patch.py`, which ship beside
202
+ it. `modeling.py` wires them into a loader with a `complete` fill in the middle entry point.
203
+ - `verify.py` builds the model, loads the weights, runs a fixed input forward, checks the
204
+ output shape and the vocabulary size, confirms the logits are finite, and compares the top
205
+ predicted tokens against a captured reference.
206
+
207
+ ## Formats
208
+
209
+ - bf16: the reference weights and the default load path in this repo (`model.safetensors`,
210
+ 3.58 GB).
211
+ - int8: the smaller on device format (`model_int8.safetensors`, about 2.08 GB), weight only int8
212
+ per channel on the large linear layers, with the Fourier filters kept in bf16. Load it in
213
+ Python with `load_model(dir, quant="int8")`, or serve it with the FELA server. The int8
214
+ forward matches the bf16 model by the golden test.
215
+
216
+ ## Model details
217
+
218
+ - Parameters: 1,788,379,536 (about 1.79 billion).
219
+ - Layers: 28, width 1536, 12 heads, feed forward hidden 8960.
220
+ - Sequence mixers: Fourier Neural Operator (512 modes), Gated-DeltaNet recall, and Landmark
221
+ global routing, in the SSSL pattern (three Fourier mixers then one recall layer), with a
222
+ Landmark layer every seventh layer.
223
+ - Tokenizer: Qwen2.5-Coder (vocabulary 151936), which carries the fill in the middle tokens.
224
+ - Warm start: Qwen2.5-Coder 1.5B, then clean room distilled, annealed, and fill in the middle
225
+ supervised fine tuned.
226
+
227
+ # Intended use, limitations, and safety
228
+
229
+ What it is for: inline code autocomplete and fill in the middle inside a local or on premises
230
+ developer tool, where running on CPU with no GPU and keeping the source on the machine matter.
231
+ It is a research preview for that use.
232
+
233
+ What it is not for: it is not a whole function generator, not a chat model, and not a reasoning
234
+ model. Do not rely on its completions for correctness or security without reading them, and do
235
+ not ship it into a product without your own evaluation. It is a 1.5B class model and will be
236
+ wrong on anything past common single line patterns.
237
+
238
+ Evaluated conditions and known failure modes:
239
+
240
+ - Strong on single line, high frequency fill in the middle; weaker on multi line blocks and
241
+ novel logic.
242
+ - The reference `modeling.py` path is bf16 and slow; the int8 FELA server is the fast path.
243
+ - No safety or license filtering of generated code is claimed. Review generated code before you
244
+ use it.
245
+
246
+ Privacy: the model runs on the device or on your own CPU, so your source does not have to leave
247
+ the machine and there is no per call API. That is the on device privacy angle.
248
+
249
+ # Model family
250
+
251
+ This is part of the FELA family from Lowdown Labs: one Fourier Neural Operator architecture
252
+ across many modalities, all CPU native and subquadratic. This repo is pushed as
253
+ `lowdown-labs/FELA-autocomplete`. Sibling repos are trained independently per modality and
254
+ share no weights, so none carries a `base_model` link.
255
+
256
+ # Acknowledgements and references
257
+
258
+ - Fourier Neural Operator: Li, Z., et al. (2021). Fourier Neural Operator for Parametric
259
+ Partial Differential Equations. ICLR. https://arxiv.org/abs/2010.08895
260
+ - Gated Linear Attention: Yang, S., et al. (2023). Gated Linear Attention Transformers with
261
+ Hardware-Efficient Training. https://arxiv.org/abs/2312.06635
262
+ - Gated DeltaNet: Yang, S., et al. (2024). Gated Delta Networks: Improving Mamba2 with Delta
263
+ Rule. https://arxiv.org/abs/2412.06464
264
+ - Landmark Attention: Mohtashami, A., Jaggi, M. (2023). Landmark Attention: Random-Access
265
+ Infinite Context Length for Transformers. https://arxiv.org/abs/2305.16300
266
+ - Qwen2.5-Coder: Hui, B., et al. (2024). Qwen2.5-Coder Technical Report.
267
+ https://arxiv.org/abs/2409.12186
268
+ - PyTorch: Paszke, A., et al. (2019). NeurIPS. https://arxiv.org/abs/1912.01703
269
+ - flash-linear-attention: the reference kernels for the Gated-DeltaNet layer.
270
+ https://github.com/fla-org/flash-linear-attention
271
+
272
+ # How to cite
273
+
274
+ ```bibtex
275
+ @misc{lowdownlabs_fela_llm,
276
+ title = {FELA LLM 1.5: An on device Fourier Neural Operator code autocomplete model},
277
+ author = {Lowdown Labs},
278
+ year = {2026},
279
+ note = {Model card}
280
+ }
281
+ ```
282
+
283
+ # License
284
+
285
+ Released under the Lowdown Labs Lovely License 1.0 (CC BY-NC 4.0 plus Hippocratic License 3.0). See LICENSE. For most LL models, a commercial license may be available; contact Lowdown Labs.
config.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "fela",
3
+ "library_name": "pytorch",
4
+ "arch": "CPUGPT (FELA LM)",
5
+ "edition": "FELA LLM 1.5 (code, 1.5B class)",
6
+ "note": "FELA code language model for fill in the middle autocomplete. 1.79B parameters. 28 layers of FNO spectral token mixers plus Gated-DeltaNet recall plus Landmark global routing (SSSL pattern). Final fill in the middle SFT model.",
7
+ "quant": "bf16",
8
+ "vocab_size": 151936,
9
+ "n_layer": 28,
10
+ "n_embd": 1536,
11
+ "n_head": 12,
12
+ "ffn_hidden": 8960,
13
+ "fno_modes": 512,
14
+ "gla_chunk": 256,
15
+ "layer_pattern": "SSSL",
16
+ "gla_delta": true,
17
+ "landmark_layer_every": 7,
18
+ "landmark_chunk": 32,
19
+ "landmark_max": 64,
20
+ "attn_layer_every": 0,
21
+ "seq_len": 2048,
22
+ "n_params": 1788379536,
23
+ "tokenizer": "Qwen2.5-Coder (tokenizer.json, vocab 151936, carries the FIM tokens <|fim_prefix|> / <|fim_suffix|> / <|fim_middle|>)",
24
+ "fim_tokens": {
25
+ "prefix": "<|fim_prefix|>",
26
+ "suffix": "<|fim_suffix|>",
27
+ "middle": "<|fim_middle|>"
28
+ },
29
+ "input_desc": "token ids from the Qwen2.5-Coder tokenizer. For fill in the middle: <|fim_prefix|> P <|fim_suffix|> S <|fim_middle|>, and the model completes the middle. For plain continuation: the prompt token ids.",
30
+ "serving": "Runs on a plain CPU with no GPU via the FELA server (int8, constant memory streaming decode, no KV cache). The bf16 model.safetensors is the reference; model_int8.safetensors is the int8 serving export.",
31
+ "int8_variant": "model_int8.safetensors",
32
+ "int8_scheme": "weight only int8 per channel symmetric on linear layers; scales fp32; kept tensors bf16; keys prefixed int8 / scale / keep",
33
+ "architectures": [
34
+ "FelaForCausalLM"
35
+ ],
36
+ "auto_map": {
37
+ "AutoConfig": "configuration_fela.FelaConfig",
38
+ "AutoModelForCausalLM": "modeling_fela.FelaForCausalLM"
39
+ }
40
+ }
configuration_fela.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import PretrainedConfig
2
+
3
+
4
+ class FelaConfig(PretrainedConfig):
5
+ model_type = "fela"
6
+
7
+ def __init__(
8
+ self,
9
+ vocab_size=151936,
10
+ seq_len=2048,
11
+ n_layer=28,
12
+ n_embd=1536,
13
+ n_head=12,
14
+ ffn_hidden=8960,
15
+ layer_pattern="SSSL",
16
+ gla_delta=True,
17
+ fno_modes=512,
18
+ gla_chunk=256,
19
+ landmark_layer_every=7,
20
+ landmark_chunk=32,
21
+ landmark_max=64,
22
+ attn_layer_every=0,
23
+ tie_word_embeddings=False,
24
+ use_cache=False,
25
+ **kwargs,
26
+ ):
27
+ self.vocab_size = vocab_size
28
+ self.seq_len = seq_len
29
+ self.n_layer = n_layer
30
+ self.n_embd = n_embd
31
+ self.n_head = n_head
32
+ self.ffn_hidden = ffn_hidden
33
+ self.layer_pattern = layer_pattern
34
+ self.gla_delta = gla_delta
35
+ self.fno_modes = fno_modes
36
+ self.gla_chunk = gla_chunk
37
+ self.landmark_layer_every = landmark_layer_every
38
+ self.landmark_chunk = landmark_chunk
39
+ self.landmark_max = landmark_max
40
+ self.attn_layer_every = attn_layer_every
41
+ self.num_hidden_layers = n_layer
42
+ self.use_cache = use_cache
43
+ super().__init__(tie_word_embeddings=tie_word_embeddings, **kwargs)
cpu_delta.py ADDED
@@ -0,0 +1,256 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ import torch
3
+ import torch.nn.functional as F
4
+
5
+
6
+ def gdn_recurrent(
7
+ q: torch.Tensor,
8
+ k: torch.Tensor,
9
+ v: torch.Tensor,
10
+ beta: torch.Tensor,
11
+ g: torch.Tensor,
12
+ scale: float,
13
+ initial_state: torch.Tensor | None = None,
14
+ ) -> tuple[torch.Tensor, torch.Tensor]:
15
+ q, k, v, beta, g = (
16
+ t.transpose(1, 2).contiguous().to(torch.float32) for t in (q, k, v, beta, g)
17
+ )
18
+ B, H, T, K = q.shape
19
+ V = v.shape[-1]
20
+ o = torch.zeros(B, H, T, V, dtype=torch.float32, device=q.device)
21
+ if initial_state is None:
22
+ h = torch.zeros(B, H, K, V, dtype=torch.float32, device=q.device)
23
+ else:
24
+ h = initial_state.to(torch.float32).clone()
25
+ q = q * scale
26
+ for i in range(T):
27
+ b_q = q[:, :, i]
28
+ b_k = k[:, :, i]
29
+ b_v = v[:, :, i].clone()
30
+ h = h * g[:, :, i].exp()[..., None, None]
31
+ b_v = b_v - (h * b_k[..., None]).sum(-2)
32
+ b_v = b_v * beta[:, :, i][..., None]
33
+ h = h + b_k.unsqueeze(-1) * b_v.unsqueeze(-2)
34
+ o[:, :, i] = torch.einsum("bhd,bhdm->bhm", b_q, h)
35
+ return (o.transpose(1, 2).contiguous(), h)
36
+
37
+
38
+ def gdn_chunk_recurrent(
39
+ q: torch.Tensor,
40
+ k: torch.Tensor,
41
+ v: torch.Tensor,
42
+ beta: torch.Tensor,
43
+ g: torch.Tensor,
44
+ scale: float,
45
+ initial_state: torch.Tensor | None = None,
46
+ chunk_size: int = 64,
47
+ ) -> tuple[torch.Tensor, torch.Tensor]:
48
+ bt = chunk_size
49
+ q, k, v, beta, g = (
50
+ t.transpose(1, 2).contiguous().to(torch.float32) for t in (q, k, v, beta, g)
51
+ )
52
+ B, H, T, K = q.shape
53
+ Vd = v.shape[-1]
54
+ pad = (bt - T % bt) % bt
55
+ if pad:
56
+ q = F.pad(q, (0, 0, 0, pad))
57
+ k = F.pad(k, (0, 0, 0, pad))
58
+ v = F.pad(v, (0, 0, 0, pad))
59
+ beta = F.pad(beta, (0, pad))
60
+ g = F.pad(g, (0, pad))
61
+ L = q.shape[2]
62
+ n = L // bt
63
+ q = q * scale
64
+ v = v * beta[..., None]
65
+ k_beta = k * beta[..., None]
66
+
67
+ def _chunks(x):
68
+ return x.reshape(B, H, n, bt, x.shape[-1])
69
+
70
+ q, k, v, k_beta = (_chunks(q), _chunks(k), _chunks(v), _chunks(k_beta))
71
+ decay = g.reshape(B, H, n, bt).cumsum(-1)
72
+ decay_exp = decay.exp()[..., None]
73
+ l_mask = (decay.unsqueeze(-1) - decay.unsqueeze(-2)).tril().exp().tril()
74
+ eye_mask = torch.triu(
75
+ torch.ones(bt, bt, dtype=torch.bool, device=q.device), diagonal=0
76
+ )
77
+ attn = -(k_beta @ k.transpose(-1, -2) * l_mask).masked_fill(eye_mask, 0)
78
+ for i in range(1, bt):
79
+ attn[..., i, :i] = attn[..., i, :i].clone() + (
80
+ attn[..., i, :i, None].clone() * attn[..., :i, :i].clone()
81
+ ).sum(-2)
82
+ attn = attn + torch.eye(bt, dtype=torch.float32, device=q.device)
83
+ v = attn @ v
84
+ k_cumdecay = attn @ (k_beta * decay_exp)
85
+ S = q.new_zeros(B, H, K, Vd)
86
+ if initial_state is not None:
87
+ S = initial_state.to(torch.float32).clone()
88
+ o = torch.zeros_like(v)
89
+ causal = torch.triu(
90
+ torch.ones(bt, bt, dtype=torch.bool, device=q.device), diagonal=1
91
+ )
92
+ for i in range(n):
93
+ q_i, k_i, v_i = (q[:, :, i], k[:, :, i], v[:, :, i])
94
+ a_i = (q_i @ k_i.transpose(-1, -2) * l_mask[:, :, i]).masked_fill(causal, 0)
95
+ v_new = v_i - k_cumdecay[:, :, i] @ S
96
+ o_inter = q_i * decay[:, :, i, :, None].exp() @ S
97
+ o[:, :, i] = o_inter + a_i @ v_new
98
+ S = (
99
+ S * decay[:, :, i, -1, None, None].exp()
100
+ + (
101
+ k_i * (decay[:, :, i, -1, None] - decay[:, :, i]).exp()[..., None]
102
+ ).transpose(-1, -2)
103
+ @ v_new
104
+ )
105
+ o = o.reshape(B, H, L, Vd)[:, :, :T]
106
+ return (o.transpose(1, 2).contiguous(), S)
107
+
108
+
109
+ def causal_depthwise_conv1d(
110
+ x: torch.Tensor,
111
+ weight: torch.Tensor,
112
+ bias: torch.Tensor | None = None,
113
+ activation: str | None = "silu",
114
+ ) -> torch.Tensor:
115
+ B, T, C = x.shape
116
+ W = weight.shape[-1]
117
+ xt = x.transpose(1, 2)
118
+ xp = F.pad(xt, (W - 1, 0))
119
+ y = F.conv1d(xp, weight, bias=bias, groups=C)[..., :T].transpose(1, 2)
120
+ if activation == "silu":
121
+ y = F.silu(y)
122
+ elif activation is not None:
123
+ raise ValueError(f"unsupported activation {activation!r}")
124
+ return y
125
+
126
+
127
+ def _conv_chunk(
128
+ p: torch.Tensor,
129
+ weight: torch.Tensor,
130
+ conv_state: torch.Tensor | None,
131
+ bias: torch.Tensor | None = None,
132
+ ) -> tuple[torch.Tensor, torch.Tensor]:
133
+ B, L, C = p.shape
134
+ W = weight.shape[-1]
135
+ pt = p.transpose(1, 2)
136
+ if conv_state is None:
137
+ conv_state = pt.new_zeros(B, C, W - 1)
138
+ full = torch.cat([conv_state, pt], dim=-1)
139
+ y = F.silu(F.conv1d(full, weight, bias=bias, groups=C)).transpose(1, 2)
140
+ return (y, full[..., -(W - 1) :])
141
+
142
+
143
+ def gdn_gate(
144
+ g: torch.Tensor, A_log: torch.Tensor, dt_bias: torch.Tensor | None = None
145
+ ) -> torch.Tensor:
146
+ g = g.float()
147
+ if dt_bias is not None:
148
+ g = g + dt_bias.float()
149
+ return -A_log.float().exp() * F.softplus(g)
150
+
151
+
152
+ def gated_rmsnorm(
153
+ o: torch.Tensor, gate: torch.Tensor, weight: torch.Tensor, eps: float = 1e-05
154
+ ) -> torch.Tensor:
155
+ o = o.float()
156
+ gate = gate.float()
157
+ rstd = torch.rsqrt(o.pow(2).mean(-1, keepdim=True) + eps)
158
+ return o * rstd * weight.float() * (gate * torch.sigmoid(gate))
159
+
160
+
161
+ class CPUGatedDeltaNet:
162
+ def __init__(self, gdn):
163
+ self.gdn = gdn
164
+ self.H = gdn.num_heads
165
+ self.Dk = gdn.head_k_dim
166
+ self.Dv = gdn.head_v_dim
167
+ self.C = gdn.value_dim
168
+ self.W = gdn.conv_size
169
+ self.scale = self.Dk ** (-0.5)
170
+ self.eps = getattr(gdn.o_norm, "eps", 1e-05)
171
+ self.chunk_size = 64
172
+ assert not gdn.allow_neg_eigval, "allow_neg_eigval not supported"
173
+ assert gdn.use_gate and gdn.use_short_conv
174
+
175
+ def _project(self, x, conv_state):
176
+ g = self.gdn
177
+ q, sq = _conv_chunk(
178
+ g.q_proj(x),
179
+ g.q_conv1d.weight,
180
+ None if conv_state is None else conv_state[0],
181
+ )
182
+ k, sk = _conv_chunk(
183
+ g.k_proj(x),
184
+ g.k_conv1d.weight,
185
+ None if conv_state is None else conv_state[1],
186
+ )
187
+ v, sv = _conv_chunk(
188
+ g.v_proj(x),
189
+ g.v_conv1d.weight,
190
+ None if conv_state is None else conv_state[2],
191
+ )
192
+ B, L = (x.shape[0], x.shape[1])
193
+ q = F.normalize(q.view(B, L, self.H, self.Dk), p=2, dim=-1)
194
+ k = F.normalize(k.view(B, L, self.H, self.Dk), p=2, dim=-1)
195
+ v = v.view(B, L, self.H, self.Dv)
196
+ beta = torch.sigmoid(g.b_proj(x))
197
+ gate_g = gdn_gate(g.a_proj(x), g.A_log, g.dt_bias)
198
+ return (q, k, v, beta, gate_g, (sq, sk, sv))
199
+
200
+ def _output(self, o, x):
201
+ g = self.gdn
202
+ B, L = (x.shape[0], x.shape[1])
203
+ gate = g.g_proj(x).view(B, L, self.H, self.Dv)
204
+ o = gated_rmsnorm(o, gate, g.o_norm.weight, self.eps)
205
+ o = o.reshape(B, L, self.C)
206
+ return g.o_proj(o)
207
+
208
+ def init_state(self, batch_size: int = 1, device=None):
209
+ if device is None:
210
+ device = self.gdn.q_proj.weight.device
211
+ return {
212
+ "recurrent_state": torch.zeros(
213
+ batch_size, self.H, self.Dk, self.Dv, device=device
214
+ ),
215
+ "conv": tuple(
216
+ (
217
+ torch.zeros(batch_size, self.C, self.W - 1, device=device)
218
+ for _ in range(3)
219
+ )
220
+ ),
221
+ }
222
+
223
+ def _recurrence(self, q, k, v, beta, g, initial_state):
224
+ if q.shape[1] >= self.chunk_size:
225
+ return gdn_chunk_recurrent(
226
+ q,
227
+ k,
228
+ v,
229
+ beta,
230
+ g,
231
+ self.scale,
232
+ initial_state=initial_state,
233
+ chunk_size=self.chunk_size,
234
+ )
235
+ return gdn_recurrent(q, k, v, beta, g, self.scale, initial_state=initial_state)
236
+
237
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
238
+ q, k, v, beta, gate_g, _ = self._project(x, None)
239
+ o, _ = self._recurrence(q, k, v, beta, gate_g, initial_state=None)
240
+ return self._output(o, x).to(x.dtype)
241
+
242
+ def forward_chunk(self, x: torch.Tensor, state):
243
+ conv_state = None if state is None else state["conv"]
244
+ rec = None if state is None else state["recurrent_state"]
245
+ q, k, v, beta, gate_g, new_conv = self._project(x, conv_state)
246
+ o, rec = self._recurrence(q, k, v, beta, gate_g, initial_state=rec)
247
+ return (
248
+ self._output(o, x).to(x.dtype),
249
+ {"recurrent_state": rec, "conv": new_conv},
250
+ )
251
+
252
+ def step(self, x: torch.Tensor, state):
253
+ if x.dim() == 2:
254
+ x = x.unsqueeze(1)
255
+ o, state = self.forward_chunk(x, state)
256
+ return (o.squeeze(1), state)
cpu_landmark.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ import torch
3
+ import torch.nn.functional as F
4
+
5
+
6
+ class CPULandmark:
7
+ def __init__(self, mixer):
8
+ self.mixer = mixer
9
+ self.H = mixer.n_head
10
+ self.D = mixer.d_head
11
+ self.C = self.H * self.D
12
+ self.chunk = mixer.chunk
13
+ self.max_land = mixer.max_land
14
+
15
+ def init_state(self, batch_size: int = 1, device=None, c: int | None = None):
16
+ if device is None:
17
+ device = self.mixer.q_proj.weight.device
18
+ return {
19
+ "k": torch.zeros(batch_size, self.max_land, self.H, self.D, device=device),
20
+ "v": torch.zeros(batch_size, self.max_land, self.H, self.D, device=device),
21
+ "n_land": torch.zeros(batch_size, dtype=torch.long, device=device),
22
+ "acc_sum": torch.zeros(batch_size, self.C, device=device),
23
+ "acc_cnt": torch.zeros(batch_size, dtype=torch.long, device=device),
24
+ "c": int(c) if c else self.chunk,
25
+ }
26
+
27
+ def _sink_kv(self, batch_size: int, device):
28
+ sink = self.mixer.sink
29
+ sk = self.mixer.k_proj(sink).reshape(1, 1, self.H, self.D)
30
+ sv = self.mixer.v_proj(sink).reshape(1, 1, self.H, self.D)
31
+ exp = (batch_size, 1, self.H, self.D)
32
+ return (sk.to(device).expand(*exp), sv.to(device).expand(*exp))
33
+
34
+ def step(self, x: torch.Tensor, state):
35
+ if x.dim() == 3:
36
+ x = x.squeeze(1)
37
+ B = x.shape[0]
38
+ if state is None:
39
+ state = self.init_state(B, device=x.device)
40
+ c = state["c"]
41
+ n = state["n_land"]
42
+ Lmax = self.max_land
43
+ sk, sv = self._sink_kv(B, x.device)
44
+ keys = torch.cat([sk, state["k"]], dim=1)
45
+ vals = torch.cat([sv, state["v"]], dim=1)
46
+ idx = torch.arange(Lmax, device=x.device)[None, :]
47
+ valid = torch.cat(
48
+ [torch.ones(B, 1, dtype=torch.bool, device=x.device), idx < n[:, None]],
49
+ dim=1,
50
+ )
51
+ q = self.mixer.q_proj(x).reshape(B, 1, self.H, self.D).transpose(1, 2)
52
+ o = F.scaled_dot_product_attention(
53
+ q,
54
+ keys.transpose(1, 2),
55
+ vals.transpose(1, 2),
56
+ attn_mask=valid[:, None, None, :],
57
+ )
58
+ o = self.mixer.o_proj(o.transpose(1, 2).reshape(B, self.C))
59
+ acc_sum = state["acc_sum"] + x
60
+ acc_cnt = state["acc_cnt"] + 1
61
+ k, v = (state["k"].clone(), state["v"].clone())
62
+ fin = acc_cnt == c
63
+ if bool(fin.any()):
64
+ mean = acc_sum / c
65
+ lk = self.mixer.k_proj(mean).reshape(B, self.H, self.D)
66
+ lv = self.mixer.v_proj(mean).reshape(B, self.H, self.D)
67
+ rows = torch.arange(B, device=x.device)
68
+ full = fin & (n >= Lmax)
69
+ if bool(full.any()):
70
+ k[full] = torch.cat([k[full][:, 1:], k[full][:, :1]], dim=1)
71
+ v[full] = torch.cat([v[full][:, 1:], v[full][:, :1]], dim=1)
72
+ write_pos = n.clamp(max=Lmax - 1)
73
+ fr, wp = (rows[fin], write_pos[fin])
74
+ k[fr, wp] = lk[fr]
75
+ v[fr, wp] = lv[fr]
76
+ n = (n + fin.long()).clamp(max=Lmax)
77
+ acc_sum = torch.where(fin[:, None], torch.zeros_like(acc_sum), acc_sum)
78
+ acc_cnt = torch.where(fin, torch.zeros_like(acc_cnt), acc_cnt)
79
+ return (
80
+ o,
81
+ {
82
+ "k": k,
83
+ "v": v,
84
+ "n_land": n,
85
+ "acc_sum": acc_sum,
86
+ "acc_cnt": acc_cnt,
87
+ "c": c,
88
+ },
89
+ )
90
+
91
+ def forward_chunk(self, x: torch.Tensor, state):
92
+ B, L, _ = x.shape
93
+ if state is None:
94
+ state = self.init_state(B, device=x.device)
95
+ outs = []
96
+ for i in range(L):
97
+ o, state = self.step(x[:, i], state)
98
+ outs.append(o)
99
+ return (torch.stack(outs, dim=1), state)
cpu_patch.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ import types
3
+ import importlib
4
+ import torch
5
+
6
+
7
+ def _sib(mod, *names):
8
+ try:
9
+ m = importlib.import_module("." + mod, __package__ or None)
10
+ except (ImportError, TypeError, ValueError):
11
+ m = importlib.import_module(mod)
12
+ return [getattr(m, n) for n in names]
13
+
14
+
15
+ (CPUGatedDeltaNet,) = _sib("cpu_delta", "CPUGatedDeltaNet")
16
+ (CPULandmark,) = _sib("cpu_landmark", "CPULandmark")
17
+ CPUSlidingWindow, swa_fused_forward = _sib(
18
+ "cpu_swa", "CPUSlidingWindow", "swa_fused_forward"
19
+ )
20
+
21
+
22
+ def _is_delta_block(block) -> bool:
23
+ return (
24
+ getattr(block, "is_gla", False)
25
+ and getattr(block.mixer, "gla_delta", False)
26
+ and hasattr(block.mixer, "gdn")
27
+ )
28
+
29
+
30
+ def _is_landmark_block(block) -> bool:
31
+ return getattr(block, "is_landmark", False)
32
+
33
+
34
+ def _has_swa_fused(block) -> bool:
35
+ return int(getattr(block.mixer, "swa_fused_window", 0)) > 0
36
+
37
+
38
+ def _make_block_methods(cpu_gdn, cpu_swa):
39
+
40
+ def step(self, x, bstate):
41
+ h = self.ln1(x)
42
+ if cpu_swa is not None:
43
+ h, bstate["swa"] = cpu_swa.step(h, bstate.get("swa"))
44
+ if h.dim() == 2:
45
+ h = h.unsqueeze(1)
46
+ o, bstate["gdn"] = cpu_gdn.step(h, bstate.get("gdn"))
47
+ x = x + o
48
+ x = x + self.ffn(self.ln2(x))
49
+ return (x, bstate)
50
+
51
+ def forward_chunk(self, x, bstate):
52
+ h = self.ln1(x)
53
+ if cpu_swa is not None:
54
+ h, bstate["swa"] = cpu_swa.forward_chunk(h, bstate.get("swa"))
55
+ o, bstate["gdn"] = cpu_gdn.forward_chunk(h, bstate.get("gdn"))
56
+ x = x + o
57
+ x = x + self.ffn(self.ln2(x))
58
+ return (x, bstate)
59
+
60
+ return (step, forward_chunk)
61
+
62
+
63
+ def _make_landmark_block_methods(cpu_land):
64
+
65
+ def step(self, x, bstate):
66
+ h = self.ln1(x)
67
+ o, bstate["land"] = cpu_land.step(h, bstate.get("land"))
68
+ x = x + o
69
+ x = x + self.ffn(self.ln2(x))
70
+ return (x, bstate)
71
+
72
+ def forward_chunk(self, x, bstate):
73
+ h = self.ln1(x)
74
+ o, bstate["land"] = cpu_land.forward_chunk(h, bstate.get("land"))
75
+ x = x + o
76
+ x = x + self.ffn(self.ln2(x))
77
+ return (x, bstate)
78
+
79
+ return (step, forward_chunk)
80
+
81
+
82
+ def _make_mixer_forward(cpu_gdn, cpu_swa, orig_forward):
83
+
84
+ def forward(self, x):
85
+ if x.is_cuda:
86
+ return orig_forward(x)
87
+ if cpu_swa is not None:
88
+ x = swa_fused_forward(self, x)
89
+ return cpu_gdn.forward(x)
90
+
91
+ return forward
92
+
93
+
94
+ def _make_init_state(model):
95
+ cfg = model.cfg
96
+ H = cfg.n_head
97
+ D = cfg.n_embd // H
98
+ M = cfg.fno_modes
99
+ C = cfg.n_embd
100
+
101
+ def init_state(self, batch_size: int = 1, device=None):
102
+ if device is None:
103
+ device = next(self.parameters()).device
104
+ states = []
105
+ for block in self.blocks:
106
+ if _is_delta_block(block):
107
+ states.append(
108
+ {"swa": None, "gdn": None}
109
+ if _has_swa_fused(block)
110
+ else {"gdn": None}
111
+ )
112
+ elif _is_landmark_block(block):
113
+ states.append({"land": None})
114
+ elif block.is_gla:
115
+ states.append(
116
+ {
117
+ "gla_state": torch.zeros(batch_size, H, D, D, device=device),
118
+ "z_norm": torch.zeros(batch_size, H, D, device=device),
119
+ }
120
+ )
121
+ else:
122
+ states.append(
123
+ {"buf": torch.zeros(batch_size, M, C, device=device), "pos": 0}
124
+ )
125
+ return states
126
+
127
+ return init_state
128
+
129
+
130
+ def enable_cpu_delta(model) -> int:
131
+ n = 0
132
+ for block in model.blocks:
133
+ if _is_delta_block(block):
134
+ cpu_gdn = CPUGatedDeltaNet(block.mixer.gdn)
135
+ cpu_swa = CPUSlidingWindow(block.mixer) if _has_swa_fused(block) else None
136
+ step, forward_chunk = _make_block_methods(cpu_gdn, cpu_swa)
137
+ block.step = types.MethodType(step, block)
138
+ block.forward_chunk = types.MethodType(forward_chunk, block)
139
+ block.mixer.forward = types.MethodType(
140
+ _make_mixer_forward(cpu_gdn, cpu_swa, block.mixer.forward), block.mixer
141
+ )
142
+ block.mixer._cpu_gdn = cpu_gdn
143
+ block.mixer._cpu_swa = cpu_swa
144
+ n += 1
145
+ elif _is_landmark_block(block):
146
+ cpu_land = CPULandmark(block.mixer)
147
+ step, forward_chunk = _make_landmark_block_methods(cpu_land)
148
+ block.step = types.MethodType(step, block)
149
+ block.forward_chunk = types.MethodType(forward_chunk, block)
150
+ block.mixer._cpu_land = cpu_land
151
+ if not hasattr(block.mixer, "prepare_inference"):
152
+ block.mixer.prepare_inference = types.MethodType(
153
+ lambda self: None, block.mixer
154
+ )
155
+ n += 1
156
+ model.init_state = types.MethodType(_make_init_state(model), model)
157
+ return n
cpu_swa.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ import torch
3
+ import torch.nn.functional as F
4
+
5
+
6
+ def banded_softmax_attention(
7
+ q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, w: int
8
+ ) -> torch.Tensor:
9
+ T = q.shape[-2]
10
+ i = torch.arange(T, device=q.device)[:, None]
11
+ j = torch.arange(T, device=q.device)[None, :]
12
+ mask = (j <= i) & (j > i - w)
13
+ return F.scaled_dot_product_attention(q, k, v, attn_mask=mask)
14
+
15
+
16
+ def swa_fused_forward(mixer, x: torch.Tensor) -> torch.Tensor:
17
+ B, T, C = x.shape
18
+ H, w = (mixer.n_head, mixer.swa_fused_window)
19
+ D = C // H
20
+ q = mixer.swa_fused_q(x).reshape(B, T, H, D).transpose(1, 2)
21
+ k = mixer.swa_fused_k(x).reshape(B, T, H, D).transpose(1, 2)
22
+ v = mixer.swa_fused_v(x).reshape(B, T, H, D).transpose(1, 2)
23
+ o = banded_softmax_attention(q, k, v, w)
24
+ o = o.transpose(1, 2).reshape(B, T, C)
25
+ return x + mixer.swa_fused_o(o)
26
+
27
+
28
+ class CPUSlidingWindow:
29
+ def __init__(self, mixer):
30
+ self.mixer = mixer
31
+ self.H = mixer.n_head
32
+ self.C = mixer.swa_fused_q.out_features
33
+ self.D = self.C // self.H
34
+ self.w = mixer.swa_fused_window
35
+ self.scale = self.D ** (-0.5)
36
+
37
+ def init_state(self, batch_size: int = 1, device=None):
38
+ if device is None:
39
+ device = self.mixer.swa_fused_q.weight.device
40
+ W1 = self.w - 1
41
+ return {
42
+ "k": torch.zeros(batch_size, W1, self.H, self.D, device=device),
43
+ "v": torch.zeros(batch_size, W1, self.H, self.D, device=device),
44
+ "n": 0,
45
+ }
46
+
47
+ def _project(self, x):
48
+ B, L, _ = x.shape
49
+ q = self.mixer.swa_fused_q(x).reshape(B, L, self.H, self.D)
50
+ k = self.mixer.swa_fused_k(x).reshape(B, L, self.H, self.D)
51
+ v = self.mixer.swa_fused_v(x).reshape(B, L, self.H, self.D)
52
+ return (q, k, v)
53
+
54
+ def forward_chunk(self, x: torch.Tensor, state):
55
+ B, L, C = x.shape
56
+ w = self.w
57
+ if state is None:
58
+ state = self.init_state(B, device=x.device)
59
+ n = state["n"]
60
+ W1 = w - 1
61
+ ck = state["k"][:, W1 - n :, :, :] if n > 0 else state["k"][:, :0]
62
+ cv = state["v"][:, W1 - n :, :, :] if n > 0 else state["v"][:, :0]
63
+ q, k, v = self._project(x)
64
+ k_all = torch.cat([ck, k], dim=1)
65
+ v_all = torch.cat([cv, v], dim=1)
66
+ Tk = n + L
67
+ iq = torch.arange(L, device=x.device)[:, None] + n
68
+ jk = torch.arange(Tk, device=x.device)[None, :]
69
+ mask = (jk <= iq) & (jk > iq - w)
70
+ qh = q.transpose(1, 2)
71
+ kh = k_all.transpose(1, 2)
72
+ vh = v_all.transpose(1, 2)
73
+ o = F.scaled_dot_product_attention(qh, kh, vh, attn_mask=mask)
74
+ o = o.transpose(1, 2).reshape(B, L, C)
75
+ out = x + self.mixer.swa_fused_o(o)
76
+ keep = min(W1, Tk)
77
+ new_k = state["k"].clone()
78
+ new_v = state["v"].clone()
79
+ if keep > 0:
80
+ new_k[:, W1 - keep :, :, :] = k_all[:, Tk - keep :, :, :]
81
+ new_v[:, W1 - keep :, :, :] = v_all[:, Tk - keep :, :, :]
82
+ new_state = {"k": new_k, "v": new_v, "n": keep}
83
+ return (out, new_state)
84
+
85
+ def step(self, x: torch.Tensor, state):
86
+ if x.dim() == 2:
87
+ x = x.unsqueeze(1)
88
+ o, state = self.forward_chunk(x, state)
89
+ return (o.squeeze(1), state)
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0edb28e43cd050f22d6960421f0c359ad23778136929285254cbb2ab084d28a9
3
+ size 3576787488
model_cpu_gpt2.py ADDED
@@ -0,0 +1,1262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ import os
3
+ from dataclasses import dataclass
4
+ from typing import Optional
5
+ import torch
6
+ import torch.nn as nn
7
+ import torch.nn.functional as F
8
+
9
+
10
+ def _detect_cpu_bf16() -> bool:
11
+ try:
12
+ with open("/proc/cpuinfo") as f:
13
+ return "avx512_bf16" in f.read()
14
+ except Exception:
15
+ return False
16
+
17
+
18
+ _CPU_HAS_BF16: bool = _detect_cpu_bf16()
19
+ try:
20
+ import pyfftw
21
+
22
+ pyfftw.interfaces.cache.enable()
23
+ pyfftw.interfaces.cache.set_keepalive_time(60.0)
24
+ except ImportError:
25
+ pass
26
+ try:
27
+ if os.environ.get("NO_CPP_EXT"):
28
+ raise ImportError("C++ extensions disabled via NO_CPP_EXT")
29
+ import torch.utils.cpp_extension as _cpp_ext
30
+
31
+ _ext_path = os.path.join(os.path.dirname(__file__), "csrc")
32
+ _gla_ext = (
33
+ _cpp_ext.load(
34
+ name="gla_scan_cpu",
35
+ sources=[os.path.join(_ext_path, "gla_scan.cpp")],
36
+ extra_cflags=["-O3", "-fopenmp", "-march=native"],
37
+ extra_ldflags=["-fopenmp"],
38
+ verbose=False,
39
+ )
40
+ if os.path.exists(os.path.join(_ext_path, "gla_scan.cpp"))
41
+ else None
42
+ )
43
+ except Exception:
44
+ _gla_ext = None
45
+ try:
46
+ if os.environ.get("NO_CPP_EXT"):
47
+ raise ImportError("C++ extensions disabled via NO_CPP_EXT")
48
+ import torch.utils.cpp_extension as _fno_cpp_ext
49
+
50
+ _fno_ext_src = os.path.join(os.path.dirname(__file__), "csrc", "fno_conv.cpp")
51
+ _fno_ext = (
52
+ _fno_cpp_ext.load(
53
+ name="fno_conv_cpu",
54
+ sources=[_fno_ext_src],
55
+ extra_cflags=["-O3", "-fopenmp", "-march=native"],
56
+ extra_ldflags=["-fopenmp"],
57
+ verbose=False,
58
+ )
59
+ if os.path.exists(_fno_ext_src)
60
+ else None
61
+ )
62
+ except Exception:
63
+ _fno_ext = None
64
+ try:
65
+ from flashfftconv import FlashFFTConv as _FlashFFTConv
66
+
67
+ _flash_fft_available = True
68
+ except ImportError:
69
+ _FlashFFTConv = None
70
+ _flash_fft_available = False
71
+ try:
72
+ from fla.ops.gla import chunk_gla as _fla_chunk_gla
73
+
74
+ _fla_available = True
75
+ try:
76
+ from fla.ops import chunk_gated_delta_rule as _fla_chunk_gdr
77
+ from fla.layers import GatedDeltaNet as _FlaGatedDeltaNet
78
+ except Exception:
79
+ _fla_chunk_gdr = None
80
+ _FlaGatedDeltaNet = None
81
+ except ImportError:
82
+ _fla_chunk_gla = None
83
+ _fla_chunk_gdr = None
84
+ _FlaGatedDeltaNet = None
85
+ _fla_available = False
86
+
87
+
88
+ def _fft_dtype_to_torch(fft_dtype: str):
89
+ return {"fp32": torch.float32, "bf16": torch.bfloat16, "fp16": torch.float16}[
90
+ fft_dtype
91
+ ]
92
+
93
+
94
+ def _gla_scan_py(q_k, k_k, v_f, chunk_gate, CS):
95
+ B, H, T, D = q_k.shape
96
+ n_chunks = T // CS
97
+ bf16 = q_k.dtype == torch.bfloat16
98
+ q_c = q_k.reshape(B, H, n_chunks, CS, D)
99
+ k_c = k_k.reshape(B, H, n_chunks, CS, D)
100
+ v_c = v_f.reshape(B, H, n_chunks, CS, D)
101
+ state = torch.zeros(B, H, D, D, device=q_k.device, dtype=torch.float32)
102
+ z_norm = torch.zeros(B, H, D, device=q_k.device, dtype=torch.float32)
103
+ chunks = []
104
+ for c in range(n_chunks):
105
+ if bf16:
106
+ num = q_c[:, :, c] @ state.bfloat16()
107
+ den = (q_c[:, :, c] @ z_norm.bfloat16().unsqueeze(-1)).clamp(min=1.0)
108
+ chunks.append(num / den)
109
+ g = chunk_gate[:, :, c, None, None]
110
+ state = g * state + (k_c[:, :, c].transpose(-2, -1) @ v_c[:, :, c]).float()
111
+ z_norm = chunk_gate[:, :, c, None] * z_norm + k_c[:, :, c].sum(-2).float()
112
+ else:
113
+ num = q_c[:, :, c] @ state
114
+ den = (q_c[:, :, c] @ z_norm.unsqueeze(-1)).clamp(min=1.0)
115
+ chunks.append(num / den)
116
+ g = chunk_gate[:, :, c, None, None]
117
+ state = g * state + k_c[:, :, c].transpose(-2, -1) @ v_c[:, :, c]
118
+ z_norm = chunk_gate[:, :, c, None] * z_norm + k_c[:, :, c].sum(dim=-2)
119
+ return torch.cat(chunks, dim=2)
120
+
121
+
122
+ @torch.compiler.disable
123
+ class _GLAScanFn(torch.autograd.Function):
124
+ @staticmethod
125
+ def forward(ctx, q_k, k_k, v_f, chunk_gate, CS):
126
+ ctx.CS = CS
127
+ with torch.no_grad():
128
+ out, states, z_norms = _gla_ext.gla_scan_fwd(
129
+ q_k.contiguous(),
130
+ k_k.contiguous(),
131
+ v_f.contiguous(),
132
+ chunk_gate.contiguous(),
133
+ CS,
134
+ )
135
+ ctx.save_for_backward(q_k, k_k, v_f, chunk_gate, states, z_norms)
136
+ return out
137
+
138
+ @staticmethod
139
+ def backward(ctx, grad_out):
140
+ q_k, k_k, v_f, chunk_gate, states, z_norms = ctx.saved_tensors
141
+ CS = ctx.CS
142
+ d_q, d_k, d_v, d_gate = _gla_ext.gla_scan_backward(
143
+ grad_out.float().contiguous(),
144
+ q_k.contiguous(),
145
+ k_k.contiguous(),
146
+ v_f.contiguous(),
147
+ chunk_gate.contiguous(),
148
+ states,
149
+ z_norms,
150
+ CS,
151
+ )
152
+ return (d_q, d_k, d_v, d_gate, None)
153
+
154
+
155
+ @dataclass
156
+ class CPUGPTConfig:
157
+ vocab_size: int = 50257
158
+ seq_len: int = 1024
159
+ n_layer: int = 12
160
+ n_embd: int = 768
161
+ n_head: int = 12
162
+ fno_modes: int = 256
163
+ gla_chunk: int = 64
164
+ ffn_hidden: int = 2048
165
+ layer_pattern: str = "SSSL"
166
+ bias: bool = False
167
+ dropout: float = 0.0
168
+ fft_dtype: str = "fp32"
169
+ use_flash_fft: bool = False
170
+ gla_delta: bool = False
171
+ sliding_window: int = 0
172
+ swa_window: int = 0
173
+ swa_fused_window: int = 0
174
+ attn_layer_every: int = 0
175
+ attn_full: bool = False
176
+ attn_window: int = 512
177
+ landmark_layer_every: int = 0
178
+ landmark_chunk: int = 32
179
+ landmark_max: int = 64
180
+ gdn_expand_v: float = 1.0
181
+ gdn_head_dim: int = 0
182
+
183
+ def __post_init__(self):
184
+ assert self.n_embd % 16 == 0, (
185
+ f"n_embd={self.n_embd} must be divisible by 16 for AMX BF16"
186
+ )
187
+ assert self.ffn_hidden % 16 == 0, (
188
+ f"ffn_hidden={self.ffn_hidden} must be divisible by 16 for AMX BF16"
189
+ )
190
+
191
+
192
+ def _layer_is_gla(layer_idx: int, n_layer: int, pattern: str) -> bool:
193
+ if pattern == "SSSL":
194
+ return layer_idx % 4 == 3
195
+ elif pattern in ("SGSG", "SLSL"):
196
+ return layer_idx % 2 == 1
197
+ elif pattern == "SSLL":
198
+ return layer_idx % 4 >= 2
199
+ elif pattern == "SLLL":
200
+ return layer_idx % 4 >= 1
201
+ elif pattern == "GLA":
202
+ return True
203
+ elif pattern == "FNO":
204
+ return False
205
+ return False
206
+
207
+
208
+ def rms_norm(x: torch.Tensor) -> torch.Tensor:
209
+ return F.rms_norm(x, (x.size(-1),))
210
+
211
+
212
+ class SwiGLU(nn.Module):
213
+ def __init__(self, cfg: CPUGPTConfig):
214
+ super().__init__()
215
+ h = cfg.ffn_hidden
216
+ d = cfg.n_embd
217
+ self.gate = nn.Linear(d, h, bias=cfg.bias)
218
+ self.up = nn.Linear(d, h, bias=cfg.bias)
219
+ self.down = nn.Linear(h, d, bias=cfg.bias)
220
+
221
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
222
+ return self.down(F.silu(self.gate(x)) * self.up(x))
223
+
224
+
225
+ @torch.compiler.disable
226
+ class _FNOConvFn(torch.autograd.Function):
227
+ @staticmethod
228
+ def forward(ctx, x, filter_td, T):
229
+ x_f = x.float().contiguous()
230
+ f_f = filter_td.float().detach().contiguous()
231
+ y, Xf, Hf = _fno_ext.fno_conv_fwd(x_f, f_f, T)
232
+ ctx.save_for_backward(Xf, Hf)
233
+ ctx.n_use = min(filter_td.shape[1], T)
234
+ ctx.M = filter_td.shape[1]
235
+ return y.to(x.dtype)
236
+
237
+ @staticmethod
238
+ def backward(ctx, grad_out):
239
+ Xf, Hf = ctx.saved_tensors
240
+ d_x, d_filter = _fno_ext.fno_conv_backward(
241
+ grad_out.float().contiguous(), Xf, Hf, ctx.n_use, ctx.M
242
+ )
243
+ return (d_x, d_filter, None)
244
+
245
+
246
+ class FNOSeqMixer(nn.Module):
247
+ def __init__(self, cfg: CPUGPTConfig):
248
+ super().__init__()
249
+ C = cfg.n_embd
250
+ M = cfg.fno_modes
251
+ self.filter_td = nn.Parameter(torch.empty(C, M))
252
+ self.out_scale = nn.Linear(C, C, bias=cfg.bias)
253
+ self.register_buffer("_hf_cache", None, persistent=False)
254
+ self._filter_version: int = -1
255
+ self._fft_dtype = _fft_dtype_to_torch(cfg.fft_dtype)
256
+ self._use_flash_fft = cfg.use_flash_fft and _flash_fft_available
257
+ self._flash_fft_conv = None
258
+ self._flash_fft_T: int = -1
259
+ nn.init.normal_(self.filter_td, std=0.02)
260
+
261
+ def _flash_conv(self, x: torch.Tensor, T: int, C: int, n_use: int) -> torch.Tensor:
262
+ if self._flash_fft_conv is None or self._flash_fft_T != T:
263
+ self._flash_fft_conv = _FlashFFTConv(2 * T, dtype=self._fft_dtype).to(
264
+ x.device
265
+ )
266
+ self._flash_fft_T = T
267
+ h = self.filter_td.new_zeros(C, 2 * T)
268
+ h[:, :n_use] = self.filter_td[:, :n_use]
269
+ xp = F.pad(x.transpose(1, 2), (0, T))
270
+ y = self._flash_fft_conv(xp.to(self._fft_dtype), h.to(self._fft_dtype))
271
+ return y[:, :, :T].transpose(1, 2).to(x.dtype)
272
+
273
+ def _rfft_conv(self, x: torch.Tensor, T: int, C: int, n_use: int) -> torch.Tensor:
274
+ dt = self._fft_dtype
275
+ h = self.filter_td.new_zeros(2 * T, C).to(dt)
276
+ h[:n_use] = self.filter_td[:, :n_use].T.to(dt)
277
+ xp = F.pad(x, (0, 0, 0, T)).to(dt)
278
+ Xf = torch.fft.rfft(xp, dim=1)
279
+ Hf = torch.fft.rfft(h, dim=0).unsqueeze(0)
280
+ Xr, Xi = (Xf.real, Xf.imag)
281
+ Hr, Hi = (Hf.real, Hf.imag)
282
+ YHf = torch.view_as_complex(
283
+ torch.stack([Xr * Hr - Xi * Hi, Xr * Hi + Xi * Hr], dim=-1).contiguous()
284
+ )
285
+ return torch.fft.irfft(YHf, n=2 * T, dim=1)[:, :T]
286
+
287
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
288
+ B, T, C = x.shape
289
+ n_use = min(self.filter_td.shape[1], T)
290
+ if _fno_ext is not None:
291
+ y = _FNOConvFn.apply(x, self.filter_td, T)
292
+ elif self._use_flash_fft and x.is_cuda:
293
+ y = self._flash_conv(x, T, C, n_use)
294
+ elif not self.training:
295
+ y = self._forward_eval_cached(x, T, C, n_use)
296
+ else:
297
+ y = self._rfft_conv(x, T, C, n_use)
298
+ return self.out_scale(y.to(x.dtype))
299
+
300
+ @torch.compiler.disable
301
+ def _forward_eval_cached(
302
+ self, x: torch.Tensor, T: int, C: int, n_use: int
303
+ ) -> torch.Tensor:
304
+ ver = self.filter_td._version
305
+ if (
306
+ self._hf_cache is None
307
+ or self._filter_version != ver
308
+ or self._hf_cache.shape[0] != T + 1
309
+ ):
310
+ h = self.filter_td.new_zeros(2 * T, C)
311
+ h[:n_use] = self.filter_td[:, :n_use].T.detach()
312
+ self._hf_cache = torch.fft.rfft(h.float(), dim=0).contiguous()
313
+ self._filter_version = ver
314
+ dt = self._fft_dtype
315
+ xp = F.pad(x, (0, 0, 0, T)).to(dt)
316
+ Xf = torch.fft.rfft(xp, dim=1)
317
+ H = self._hf_cache.unsqueeze(0)
318
+ Xr, Xi, Hr, Hi = (Xf.real, Xf.imag, H.real, H.imag)
319
+ YHf = torch.view_as_complex(
320
+ torch.stack([Xr * Hr - Xi * Hi, Xr * Hi + Xi * Hr], dim=-1).contiguous()
321
+ )
322
+ return torch.fft.irfft(YHf, n=2 * T, dim=1)[:, :T]
323
+
324
+ def prepare_inference(self):
325
+ if hasattr(self, "_h_td"):
326
+ return
327
+ with torch.no_grad():
328
+ pass
329
+
330
+ def _build_h_td(self, T: int) -> None:
331
+ if hasattr(self, "_h_td"):
332
+ return
333
+ C = self.filter_td.shape[0]
334
+ M = min(self.filter_td.shape[1], T)
335
+ with torch.no_grad():
336
+ h_td = self.filter_td[:, :M].T.float().contiguous()
337
+ self.register_buffer("_h_td", h_td.contiguous())
338
+
339
+ def step(
340
+ self, x: torch.Tensor, buf: torch.Tensor, pos: int
341
+ ) -> tuple[torch.Tensor, int]:
342
+ M = buf.shape[1]
343
+ self._build_h_td(M)
344
+ slot = pos % M
345
+ buf[:, slot, :] = x.detach()
346
+ indices = torch.arange(M, device=x.device)
347
+ ordered_idx = (slot + 1 + indices) % M
348
+ ordered = buf[:, ordered_idx, :]
349
+ h_flip = self._h_td.flip(0)
350
+ y = (h_flip.unsqueeze(0) * ordered).sum(1)
351
+ return (self.out_scale(y.to(x.dtype)), pos + 1)
352
+
353
+ def forward_chunk(self, h: torch.Tensor, ctx):
354
+ B, L, C = h.shape
355
+ M = self.filter_td.shape[1]
356
+ if ctx is None:
357
+ ctx = h.new_zeros(B, M - 1, C)
358
+ full = torch.cat([ctx, h], dim=1)
359
+ P = full.shape[1]
360
+ n_use = min(M, P)
361
+ dt = self._fft_dtype
362
+ hk = self.filter_td.new_zeros(2 * P, C).to(dt)
363
+ hk[:n_use] = self.filter_td[:, :n_use].T.to(dt)
364
+ xp = F.pad(full, (0, 0, 0, P)).to(dt)
365
+ Xf = torch.fft.rfft(xp, dim=1)
366
+ Hf = torch.fft.rfft(hk, dim=0).unsqueeze(0)
367
+ Xr, Xi, Hr, Hi = (Xf.real, Xf.imag, Hf.real, Hf.imag)
368
+ YHf = torch.view_as_complex(
369
+ torch.stack([Xr * Hr - Xi * Hi, Xr * Hi + Xi * Hr], dim=-1).contiguous()
370
+ )
371
+ y = torch.fft.irfft(YHf, n=2 * P, dim=1)[:, M - 1 : P]
372
+ return (self.out_scale(y.to(h.dtype)), full[:, -(M - 1) :, :])
373
+
374
+
375
+ class GLAMixer(nn.Module):
376
+ def __init__(self, cfg: CPUGPTConfig):
377
+ super().__init__()
378
+ H = cfg.n_head
379
+ D = cfg.n_embd // H
380
+ C = cfg.n_embd
381
+ CS = cfg.gla_chunk
382
+ assert C % H == 0, "n_embd must be divisible by n_head"
383
+ assert cfg.seq_len % CS == 0, (
384
+ f"seq_len {cfg.seq_len} must be divisible by gla_chunk {CS}"
385
+ )
386
+ self.n_head = H
387
+ self.d_head = D
388
+ self.chunk = CS
389
+ self.gla_delta = bool(getattr(cfg, "gla_delta", False))
390
+ self.sliding_window = int(getattr(cfg, "sliding_window", 0))
391
+ self.swa_window = int(getattr(cfg, "swa_window", 0))
392
+ if self.swa_window > 0:
393
+ self.swa_q = nn.Linear(C, C, bias=False)
394
+ self.swa_k = nn.Linear(C, C, bias=False)
395
+ self.swa_v = nn.Linear(C, C, bias=False)
396
+ self.swa_o = nn.Linear(C, C, bias=False)
397
+ self.swa_fused_window = int(getattr(cfg, "swa_fused_window", 0))
398
+ if self.swa_fused_window > 0:
399
+ self.swa_fused_q = nn.Linear(C, C, bias=False)
400
+ self.swa_fused_k = nn.Linear(C, C, bias=False)
401
+ self.swa_fused_v = nn.Linear(C, C, bias=False)
402
+ self.swa_fused_o = nn.Linear(C, C, bias=False)
403
+ nn.init.zeros_(self.swa_fused_o.weight)
404
+ self.gdn_expand_v = float(getattr(cfg, "gdn_expand_v", 1.0))
405
+ self.gdn_head_dim = int(getattr(cfg, "gdn_head_dim", 0)) or D
406
+ if not (self.gla_delta and _FlaGatedDeltaNet is not None):
407
+ self.q_proj = nn.Linear(C, C, bias=False)
408
+ self.k_proj = nn.Linear(C, C, bias=False)
409
+ self.v_proj = nn.Linear(C, C, bias=False)
410
+ self.g_proj = nn.Linear(C, H, bias=True)
411
+ self.out_proj = nn.Linear(C, C, bias=False)
412
+ self.gla_scale = nn.Parameter(torch.ones(H) * 0.5)
413
+ nn.init.constant_(self.g_proj.bias, -4.0)
414
+ self._swa_mask = None
415
+ if self.gla_delta and _FlaGatedDeltaNet is not None:
416
+ self.gdn = _FlaGatedDeltaNet(
417
+ hidden_size=C,
418
+ num_heads=H,
419
+ head_dim=self.gdn_head_dim,
420
+ expand_v=self.gdn_expand_v,
421
+ mode="chunk",
422
+ )
423
+
424
+ def _python_scan(
425
+ self,
426
+ q_k: torch.Tensor,
427
+ k_k: torch.Tensor,
428
+ v: torch.Tensor,
429
+ chunk_gate: torch.Tensor,
430
+ ) -> torch.Tensor:
431
+ B, H, T, D = q_k.shape
432
+ CS = self.chunk
433
+ n_chunks = T // CS
434
+ q_c = q_k.view(B, H, n_chunks, CS, D)
435
+ k_c = k_k.view(B, H, n_chunks, CS, D)
436
+ v_c = v.view(B, H, n_chunks, CS, D)
437
+ state = torch.zeros(B, H, D, D, device=q_k.device, dtype=q_k.dtype)
438
+ z_norm = torch.zeros(B, H, D, device=q_k.device, dtype=q_k.dtype)
439
+ out = torch.zeros(B, H, T, D, device=q_k.device, dtype=q_k.dtype)
440
+ for c in range(n_chunks):
441
+ num = q_c[:, :, c] @ state
442
+ den = (q_c[:, :, c] @ z_norm.unsqueeze(-1)).clamp(min=1.0)
443
+ out[:, :, c * CS : (c + 1) * CS] = num / den
444
+ g = chunk_gate[:, :, c, None, None]
445
+ state = g * state + k_c[:, :, c].transpose(-2, -1) @ v_c[:, :, c]
446
+ z_norm = chunk_gate[:, :, c, None] * z_norm + k_c[:, :, c].sum(dim=-2)
447
+ return out
448
+
449
+ def _swa(self, q, k, v):
450
+ B, H, T, D = q.shape
451
+ w = self.sliding_window
452
+ pad = (w - T % w) % w
453
+ if pad:
454
+ q, k, v = (F.pad(t, (0, 0, 0, pad)) for t in (q, k, v))
455
+ Tp = T + pad
456
+ nb = Tp // w
457
+ qb = q.reshape(B, H, nb, w, D)
458
+ kb = k.reshape(B, H, nb, w, D)
459
+ vb = v.reshape(B, H, nb, w, D)
460
+ kc = torch.cat([F.pad(kb, (0, 0, 0, 0, 1, 0))[:, :, :-1], kb], dim=3)
461
+ vc = torch.cat([F.pad(vb, (0, 0, 0, 0, 1, 0))[:, :, :-1], vb], dim=3)
462
+ if (
463
+ self._swa_mask is None
464
+ or self._swa_mask.shape[-1] != 2 * w
465
+ or self._swa_mask.device != q.device
466
+ ):
467
+ i = torch.arange(w, device=q.device)
468
+ j = torch.arange(2 * w, device=q.device)
469
+ self._swa_mask = (j[None, :] < w) | (j[None, :] - w <= i[:, None])
470
+ out = F.scaled_dot_product_attention(qb, kc, vc, attn_mask=self._swa_mask)
471
+ return out.reshape(B, H, Tp, D)[:, :, :T]
472
+
473
+ def _swa_exact(self, x: torch.Tensor) -> torch.Tensor:
474
+ B, T, C = x.shape
475
+ H, D, w = (self.n_head, self.d_head, self.swa_window)
476
+ q = self.swa_q(x).reshape(B, T, H, D).transpose(1, 2)
477
+ k = self.swa_k(x).reshape(B, T, H, D).transpose(1, 2)
478
+ v = self.swa_v(x).reshape(B, T, H, D).transpose(1, 2)
479
+ i = torch.arange(T, device=x.device)[:, None]
480
+ j = torch.arange(T, device=x.device)[None, :]
481
+ mask = (j <= i) & (j > i - w)
482
+ o = F.scaled_dot_product_attention(q, k, v, attn_mask=mask)
483
+ o = o.transpose(1, 2).reshape(B, T, C)
484
+ return self.swa_o(o)
485
+
486
+ def _swa_fused(self, x: torch.Tensor) -> torch.Tensor:
487
+ B, T, C = x.shape
488
+ H, D, w = (self.n_head, self.d_head, self.swa_fused_window)
489
+ q = self.swa_fused_q(x).reshape(B, T, H, D).transpose(1, 2)
490
+ k = self.swa_fused_k(x).reshape(B, T, H, D).transpose(1, 2)
491
+ v = self.swa_fused_v(x).reshape(B, T, H, D).transpose(1, 2)
492
+ i = torch.arange(T, device=x.device)[:, None]
493
+ j = torch.arange(T, device=x.device)[None, :]
494
+ mask = (j <= i) & (j > i - w)
495
+ o = F.scaled_dot_product_attention(q, k, v, attn_mask=mask)
496
+ o = o.transpose(1, 2).reshape(B, T, C)
497
+ return x + self.swa_fused_o(o)
498
+
499
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
500
+ B, T, C = x.shape
501
+ H, D, CS = (self.n_head, self.d_head, self.chunk)
502
+ if self.gla_delta and hasattr(self, "gdn") and x.is_cuda:
503
+ gdn_in = self._swa_fused(x) if self.swa_fused_window > 0 else x
504
+ o = self.gdn(gdn_in)
505
+ o = o[0] if isinstance(o, tuple) else o
506
+ if self.swa_window > 0:
507
+ o = o + self._swa_exact(x)
508
+ return o
509
+ q = self.q_proj(x).reshape(B, T, H, D).transpose(1, 2)
510
+ k = self.k_proj(x).reshape(B, T, H, D).transpose(1, 2)
511
+ v = self.v_proj(x).reshape(B, T, H, D).transpose(1, 2)
512
+ if x.is_cuda and _fla_available:
513
+ log_g = -F.softplus(self.g_proj(x))
514
+ q_t = q.transpose(1, 2).contiguous()
515
+ k_t = k.transpose(1, 2).contiguous()
516
+ v_t = v.transpose(1, 2).contiguous()
517
+ if self.gla_delta and _fla_chunk_gdr is not None:
518
+ beta = self.beta_proj(x).float()
519
+ g_delta = self.a_proj(x).float()
520
+ y_fla, _ = _fla_chunk_gdr(
521
+ q_t,
522
+ k_t,
523
+ v_t,
524
+ g=g_delta,
525
+ beta=beta,
526
+ scale=D ** (-0.5),
527
+ output_final_state=False,
528
+ use_beta_sigmoid_in_kernel=True,
529
+ use_qk_l2norm_in_kernel=True,
530
+ )
531
+ else:
532
+ g_t = log_g.unsqueeze(-1).expand(B, T, H, D).contiguous().float()
533
+ y_fla, _ = _fla_chunk_gla(
534
+ q_t, k_t, v_t, g=g_t, scale=D ** (-0.5), output_final_state=False
535
+ )
536
+ y = y_fla.to(x.dtype).transpose(1, 2).reshape(B, T, C)
537
+ if self.sliding_window > 0:
538
+ y = y + self._swa(q, k, v).transpose(1, 2).reshape(B, T, C)
539
+ return self.out_proj(y)
540
+ n_chunks = T // CS
541
+ BH = B * H
542
+ q_l = q.reshape(BH, n_chunks, CS, D)
543
+ k_l = k.reshape(BH, n_chunks, CS, D)
544
+ v_l = v.reshape(BH, n_chunks, CS, D)
545
+ y_local = F.scaled_dot_product_attention(q_l, k_l, v_l, is_causal=True).reshape(
546
+ B, H, T, D
547
+ )
548
+ if _CPU_HAS_BF16 and q.dtype == torch.bfloat16:
549
+ q_k = F.elu(q) + 1.0
550
+ k_k = F.elu(k) + 1.0
551
+ v_f = v
552
+ else:
553
+ q_k = F.elu(q.float()) + 1.0
554
+ k_k = F.elu(k.float()) + 1.0
555
+ v_f = v.float()
556
+ log_g = -F.softplus(self.g_proj(x).float())
557
+ log_g = log_g.transpose(1, 2)
558
+ chunk_log_g = log_g.reshape(B, H, n_chunks, CS).sum(-1)
559
+ chunk_gate = chunk_log_g.exp()
560
+ if _gla_ext is not None and q_k.dtype == torch.float32 and (not q_k.is_cuda):
561
+ y_gla = _GLAScanFn.apply(q_k, k_k, v_f, chunk_gate, CS)
562
+ else:
563
+ y_gla = _gla_scan_py(q_k, k_k, v_f, chunk_gate, CS)
564
+ scale = self.gla_scale.reshape(1, H, 1, 1).to(x.dtype)
565
+ y = (y_local + scale * y_gla.to(x.dtype)).transpose(1, 2).reshape(B, T, C)
566
+ return self.out_proj(y)
567
+
568
+ def step(
569
+ self, x: torch.Tensor, state: torch.Tensor, z_norm: torch.Tensor
570
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
571
+ B = x.shape[0]
572
+ H, D = (self.n_head, self.d_head)
573
+ q = self.q_proj(x).view(B, H, D)
574
+ k = self.k_proj(x).view(B, H, D)
575
+ v = self.v_proj(x).view(B, H, D)
576
+ log_g = -F.softplus(self.g_proj(x).float())
577
+ scale = D ** (-0.5)
578
+ if x.is_cuda and _fla_available:
579
+ st = (
580
+ state
581
+ if torch.is_tensor(state) and state.dtype == torch.float32
582
+ else None
583
+ )
584
+ g_t = log_g.view(B, 1, H, 1).expand(B, 1, H, D).contiguous()
585
+ y, state = _fla_chunk_gla(
586
+ q.view(B, 1, H, D),
587
+ k.view(B, 1, H, D),
588
+ v.view(B, 1, H, D),
589
+ g=g_t,
590
+ scale=scale,
591
+ initial_state=st,
592
+ output_final_state=True,
593
+ )
594
+ y = y.reshape(B, H * D).to(x.dtype)
595
+ return (self.out_proj(y), state, z_norm)
596
+ gate = log_g.exp()
597
+ qf, kf, vf = (q.float(), k.float(), v.float())
598
+ if not (torch.is_tensor(state) and state.dim() == 4):
599
+ state = torch.zeros(B, H, D, D, device=x.device, dtype=torch.float32)
600
+ state = gate.unsqueeze(-1).unsqueeze(-1) * state + torch.einsum(
601
+ "bhd,bhe->bhde", kf, vf
602
+ )
603
+ y_gla = torch.einsum("bhd,bhde->bhe", qf, state) * scale
604
+ return (self.out_proj(y_gla.reshape(B, H * D).to(x.dtype)), state, z_norm)
605
+
606
+ def forward_chunk(self, h: torch.Tensor, state):
607
+ B, L, C = h.shape
608
+ H, D = (self.n_head, self.d_head)
609
+ q = self.q_proj(h).view(B, L, H, D)
610
+ k = self.k_proj(h).view(B, L, H, D)
611
+ v = self.v_proj(h).view(B, L, H, D)
612
+ log_g = -F.softplus(self.g_proj(h).float())
613
+ g = log_g.unsqueeze(-1).expand(B, L, H, D).contiguous()
614
+ st = state if torch.is_tensor(state) and state.dtype == torch.float32 else None
615
+ y, state = _fla_chunk_gla(
616
+ q, k, v, g=g, scale=D ** (-0.5), initial_state=st, output_final_state=True
617
+ )
618
+ return (self.out_proj(y.reshape(B, L, C).to(h.dtype)), state)
619
+
620
+
621
+ class AttnMixer(nn.Module):
622
+ def __init__(self, cfg: CPUGPTConfig):
623
+ super().__init__()
624
+ C, H = (cfg.n_embd, cfg.n_head)
625
+ assert C % H == 0
626
+ self.n_head = H
627
+ self.d_head = C // H
628
+ self.full = bool(getattr(cfg, "attn_full", False))
629
+ self.window = int(getattr(cfg, "attn_window", 512))
630
+ self.q_proj = nn.Linear(C, C, bias=False)
631
+ self.k_proj = nn.Linear(C, C, bias=False)
632
+ self.v_proj = nn.Linear(C, C, bias=False)
633
+ self.o_proj = nn.Linear(C, C, bias=False)
634
+
635
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
636
+ B, T, C = x.shape
637
+ H, D = (self.n_head, self.d_head)
638
+ q = self.q_proj(x).reshape(B, T, H, D).transpose(1, 2)
639
+ k = self.k_proj(x).reshape(B, T, H, D).transpose(1, 2)
640
+ v = self.v_proj(x).reshape(B, T, H, D).transpose(1, 2)
641
+ if self.full:
642
+ o = F.scaled_dot_product_attention(q, k, v, is_causal=True)
643
+ else:
644
+ w = self.window
645
+ i = torch.arange(T, device=x.device)[:, None]
646
+ j = torch.arange(T, device=x.device)[None, :]
647
+ mask = (j <= i) & (j > i - w)
648
+ o = F.scaled_dot_product_attention(q, k, v, attn_mask=mask)
649
+ o = o.transpose(1, 2).reshape(B, T, C)
650
+ return self.o_proj(o)
651
+
652
+
653
+ def _layer_is_attn(layer_idx: int, cfg: CPUGPTConfig) -> bool:
654
+ k = int(getattr(cfg, "attn_layer_every", 0))
655
+ if k <= 0:
656
+ return False
657
+ return (layer_idx + 1) % k == 0
658
+
659
+
660
+ class LandmarkMixer(nn.Module):
661
+ def __init__(self, cfg: CPUGPTConfig):
662
+ super().__init__()
663
+ C, H = (cfg.n_embd, cfg.n_head)
664
+ assert C % H == 0
665
+ self.n_head = H
666
+ self.d_head = C // H
667
+ self.chunk = max(1, int(getattr(cfg, "landmark_chunk", 32)))
668
+ self.max_land = max(1, int(getattr(cfg, "landmark_max", 64)))
669
+ self.q_proj = nn.Linear(C, C, bias=False)
670
+ self.k_proj = nn.Linear(C, C, bias=False)
671
+ self.v_proj = nn.Linear(C, C, bias=False)
672
+ self.o_proj = nn.Linear(C, C, bias=False)
673
+ self.sink = nn.Parameter(torch.zeros(1, 1, C))
674
+
675
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
676
+ B, T, C = x.shape
677
+ H, D = (self.n_head, self.d_head)
678
+ c = max(self.chunk, (T + self.max_land - 1) // self.max_land)
679
+ nc = (T + c - 1) // c
680
+ pad = nc * c - T
681
+ xp = F.pad(x, (0, 0, 0, pad))
682
+ land = xp.view(B, nc, c, C).mean(2)
683
+ land = torch.cat([self.sink.expand(B, 1, C), land], dim=1)
684
+ q = self.q_proj(x).reshape(B, T, H, D).transpose(1, 2)
685
+ k = self.k_proj(land).reshape(B, nc + 1, H, D).transpose(1, 2)
686
+ v = self.v_proj(land).reshape(B, nc + 1, H, D).transpose(1, 2)
687
+ tok_c = (torch.arange(T, device=x.device) // c)[:, None]
688
+ land_i = torch.arange(nc, device=x.device)[None, :]
689
+ sink_ok = torch.ones(T, 1, dtype=torch.bool, device=x.device)
690
+ chunk_ok = land_i < tok_c
691
+ mask = torch.cat([sink_ok, chunk_ok], dim=1).view(1, 1, T, nc + 1)
692
+ o = F.scaled_dot_product_attention(q, k, v, attn_mask=mask)
693
+ o = o.transpose(1, 2).reshape(B, T, C)
694
+ return self.o_proj(o)
695
+
696
+
697
+ def _layer_is_landmark(layer_idx: int, cfg: CPUGPTConfig) -> bool:
698
+ k = int(getattr(cfg, "landmark_layer_every", 0))
699
+ return k > 0 and (layer_idx + 1) % k == 0
700
+
701
+
702
+ class CPUGPTBlock(nn.Module):
703
+ def __init__(self, cfg: CPUGPTConfig, layer_idx: int):
704
+ super().__init__()
705
+ is_landmark = _layer_is_landmark(layer_idx, cfg)
706
+ is_attn = not is_landmark and _layer_is_attn(layer_idx, cfg)
707
+ is_gla = (
708
+ not is_landmark
709
+ and (not is_attn)
710
+ and _layer_is_gla(layer_idx, cfg.n_layer, cfg.layer_pattern)
711
+ )
712
+ if is_landmark:
713
+ self.mixer = LandmarkMixer(cfg)
714
+ elif is_attn:
715
+ self.mixer = AttnMixer(cfg)
716
+ else:
717
+ self.mixer = GLAMixer(cfg) if is_gla else FNOSeqMixer(cfg)
718
+ self.ffn = SwiGLU(cfg)
719
+ self.ln1 = nn.RMSNorm(cfg.n_embd)
720
+ self.ln2 = nn.RMSNorm(cfg.n_embd)
721
+ self.is_gla = is_gla
722
+ self.is_attn = is_attn
723
+ self.is_landmark = is_landmark
724
+
725
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
726
+ x = x + self.mixer(self.ln1(x))
727
+ x = x + self.ffn(self.ln2(x))
728
+ return x
729
+
730
+ def step(self, x: torch.Tensor, bstate: dict) -> tuple:
731
+ h = self.ln1(x)
732
+ mixer = self.mixer
733
+ if isinstance(mixer, (AttnMixer, LandmarkMixer)):
734
+ raise NotImplementedError(
735
+ "AttnMixer/LandmarkMixer recurrent step() not implemented; use forward()"
736
+ )
737
+ if isinstance(mixer, GLAMixer):
738
+ h_out, bstate["gla_state"], bstate["z_norm"] = mixer.step(
739
+ h, bstate["gla_state"], bstate["z_norm"]
740
+ )
741
+ else:
742
+ assert isinstance(mixer, FNOSeqMixer)
743
+ h_out, bstate["pos"] = mixer.step(h, bstate["buf"], bstate["pos"])
744
+ x = x + h_out
745
+ x = x + self.ffn(self.ln2(x))
746
+ return (x, bstate)
747
+
748
+ def forward_chunk(self, x: torch.Tensor, bstate: dict) -> tuple:
749
+ h = self.ln1(x)
750
+ mixer = self.mixer
751
+ if isinstance(mixer, (AttnMixer, LandmarkMixer)):
752
+ raise NotImplementedError(
753
+ "AttnMixer/LandmarkMixer forward_chunk() not implemented; use forward()"
754
+ )
755
+ if isinstance(mixer, GLAMixer):
756
+ h_out, bstate["gla_state"] = mixer.forward_chunk(h, bstate.get("gla_state"))
757
+ else:
758
+ assert isinstance(mixer, FNOSeqMixer)
759
+ h_out, bstate["fno_ctx"] = mixer.forward_chunk(h, bstate.get("fno_ctx"))
760
+ x = x + h_out
761
+ x = x + self.ffn(self.ln2(x))
762
+ return (x, bstate)
763
+
764
+
765
+ class _ChunkedCEFn(torch.autograd.Function):
766
+ @staticmethod
767
+ def forward(ctx, x_flat, weight, targets, chunk_size):
768
+ N = x_flat.shape[0]
769
+ use_bf16 = x_flat.dtype == torch.bfloat16
770
+ count = (targets != -1).sum().clamp(min=1)
771
+ w_g = weight.bfloat16() if use_bf16 else weight.float()
772
+ loss_sum = torch.zeros((), dtype=torch.float32, device=x_flat.device)
773
+ for start in range(0, N, chunk_size):
774
+ end = min(start + chunk_size, N)
775
+ x_c = x_flat[start:end]
776
+ t_c = targets[start:end]
777
+ logits_c = (x_c @ w_g.T).float()
778
+ logits_c = 15.0 * torch.tanh(logits_c * (1.0 / 15.0))
779
+ loss_sum = loss_sum + F.cross_entropy(
780
+ logits_c, t_c, ignore_index=-1, reduction="sum"
781
+ )
782
+ ctx.save_for_backward(x_flat, weight, targets)
783
+ ctx.chunk_size = chunk_size
784
+ ctx.count = int(count.item())
785
+ ctx.use_bf16 = use_bf16
786
+ return loss_sum / count
787
+
788
+ @staticmethod
789
+ def backward(ctx, grad_out):
790
+ x_flat, weight, targets = ctx.saved_tensors
791
+ N, C = x_flat.shape
792
+ chunk_size = ctx.chunk_size
793
+ use_bf16 = ctx.use_bf16
794
+ scale = grad_out.item() / ctx.count
795
+ w_g = weight.bfloat16() if use_bf16 else weight.float()
796
+ dx = torch.zeros(N, C, dtype=torch.float32, device=x_flat.device)
797
+ dw = torch.zeros_like(weight, dtype=torch.float32)
798
+ for start in range(0, N, chunk_size):
799
+ end = min(start + chunk_size, N)
800
+ x_c = x_flat[start:end]
801
+ t_c = targets[start:end]
802
+ logits_c = (x_c @ w_g.T).float()
803
+ tanh_c = torch.tanh(logits_c * (1.0 / 15.0))
804
+ logits_cap = 15.0 * tanh_c
805
+ probs_c = torch.softmax(logits_cap, dim=-1)
806
+ valid = t_c != -1
807
+ probs_c[~valid] = 0.0
808
+ if valid.any():
809
+ probs_c[valid, t_c[valid]] -= 1.0
810
+ d_logits_c = probs_c * (1.0 - tanh_c * tanh_c) * scale
811
+ if use_bf16:
812
+ dx[start:end] = (d_logits_c.bfloat16() @ w_g).float()
813
+ else:
814
+ dx[start:end] = d_logits_c @ w_g
815
+ if use_bf16:
816
+ dw.add_((d_logits_c.bfloat16().T @ x_c).float())
817
+ else:
818
+ dw.addmm_(d_logits_c.T, x_c)
819
+ return (dx.to(x_flat.dtype), dw, None, None)
820
+
821
+
822
+ def _chunked_cross_entropy(
823
+ weight: torch.Tensor, x: torch.Tensor, targets: torch.Tensor, chunk: int = 512
824
+ ) -> torch.Tensor:
825
+ B, T, C = x.shape
826
+ return _ChunkedCEFn.apply(
827
+ x.reshape(B * T, C), weight, targets.reshape(B * T), chunk
828
+ )
829
+
830
+
831
+ class CPUGPT(nn.Module):
832
+ def __init__(self, cfg: CPUGPTConfig):
833
+ super().__init__()
834
+ self.cfg = cfg
835
+ self.wte = nn.Embedding(cfg.vocab_size, cfg.n_embd)
836
+ self.blocks = nn.ModuleList([CPUGPTBlock(cfg, i) for i in range(cfg.n_layer)])
837
+ self.ln_out = nn.RMSNorm(cfg.n_embd)
838
+ self.lm_head = nn.Linear(cfg.n_embd, cfg.vocab_size, bias=False)
839
+ self.lm_head.weight = self.wte.weight
840
+ self._init_weights()
841
+
842
+ def _init_weights(self):
843
+ nn.init.normal_(self.wte.weight, std=0.02)
844
+ for block in self.blocks:
845
+ if isinstance(block.mixer, GLAMixer) and hasattr(block.mixer, "q_proj"):
846
+ for proj in [
847
+ block.mixer.q_proj,
848
+ block.mixer.k_proj,
849
+ block.mixer.v_proj,
850
+ block.mixer.out_proj,
851
+ ]:
852
+ nn.init.normal_(proj.weight, std=0.02)
853
+ if isinstance(block.mixer, AttnMixer):
854
+ for proj in [
855
+ block.mixer.q_proj,
856
+ block.mixer.k_proj,
857
+ block.mixer.v_proj,
858
+ block.mixer.o_proj,
859
+ ]:
860
+ nn.init.normal_(proj.weight, std=0.02)
861
+ nn.init.normal_(block.ffn.gate.weight, std=0.02)
862
+ nn.init.normal_(block.ffn.up.weight, std=0.02)
863
+ nn.init.zeros_(block.ffn.down.weight)
864
+
865
+ def param_count(self) -> int:
866
+ return sum((p.numel() for p in self.parameters()))
867
+
868
+ def prepare_inference(self):
869
+ for block in self.blocks:
870
+ if not block.is_gla and (not getattr(block, "is_attn", False)):
871
+ block.mixer.prepare_inference()
872
+
873
+ def init_state(self, batch_size: int = 1, device=None) -> list:
874
+ if device is None:
875
+ device = next(self.parameters()).device
876
+ H = self.cfg.n_head
877
+ D = self.cfg.n_embd // H
878
+ M = self.cfg.fno_modes
879
+ C = self.cfg.n_embd
880
+ states = []
881
+ for block in self.blocks:
882
+ if getattr(block, "is_attn", False):
883
+ states.append({})
884
+ elif block.is_gla:
885
+ states.append(
886
+ {
887
+ "gla_state": torch.zeros(batch_size, H, D, D, device=device),
888
+ "z_norm": torch.zeros(batch_size, H, D, device=device),
889
+ }
890
+ )
891
+ else:
892
+ states.append(
893
+ {"buf": torch.zeros(batch_size, M, C, device=device), "pos": 0}
894
+ )
895
+ return states
896
+
897
+ @torch.no_grad()
898
+ def step(self, idx: torch.Tensor, states: list):
899
+ x = self.wte(idx.unsqueeze(1) if idx.dim() == 1 else idx)
900
+ if x.dim() == 3:
901
+ x = x.squeeze(1)
902
+ x = F.rms_norm(x, (x.size(-1),))
903
+ new_states = []
904
+ for block, bstate in zip(self.blocks, states):
905
+ x, bstate = block.step(x, bstate)
906
+ new_states.append(bstate)
907
+ x = self.ln_out(x)
908
+ logits = self.lm_head(x.unsqueeze(1)).squeeze(1).float()
909
+ logits = 15.0 * torch.tanh(logits / 15.0)
910
+ return (logits, new_states)
911
+
912
+ @torch.no_grad()
913
+ def prefill_chunked(self, idx: torch.Tensor, chunk_size: int = 512):
914
+ B, T = idx.shape
915
+ states = [dict() for _ in self.blocks]
916
+ last_hidden = None
917
+ for c0 in range(0, T, chunk_size):
918
+ x = self.wte(idx[:, c0 : c0 + chunk_size])
919
+ x = F.rms_norm(x, (x.size(-1),))
920
+ for l, block in enumerate(self.blocks):
921
+ x, states[l] = block.forward_chunk(x, states[l])
922
+ last_hidden = x[:, -1:, :]
923
+ logits = self.lm_head(self.ln_out(last_hidden)).float()
924
+ logits = 15.0 * torch.tanh(logits / 15.0)
925
+ return (logits.squeeze(1), states)
926
+
927
+ @torch.no_grad()
928
+ def generate(
929
+ self,
930
+ prompt_ids: torch.Tensor,
931
+ max_new_tokens: int = 200,
932
+ temperature: float = 0.8,
933
+ top_k: int = 50,
934
+ ) -> torch.Tensor:
935
+ self.prepare_inference()
936
+ device = prompt_ids.device
937
+ states = self.init_state(batch_size=1, device=device)
938
+ logits = torch.zeros(prompt_ids.shape[0], self.cfg.vocab_size, device=device)
939
+ for i in range(prompt_ids.shape[1]):
940
+ tok = prompt_ids[:, i]
941
+ logits, states = self.step(tok, states)
942
+ generated = []
943
+ for _ in range(max_new_tokens):
944
+ if temperature == 0.0:
945
+ next_tok = logits.argmax(dim=-1)
946
+ else:
947
+ scaled = logits / temperature
948
+ if top_k > 0:
949
+ topk_vals, _ = torch.topk(scaled, top_k)
950
+ scaled = scaled.masked_fill(
951
+ scaled < topk_vals[:, -1:], float("-inf")
952
+ )
953
+ probs = torch.softmax(scaled, dim=-1)
954
+ next_tok = torch.multinomial(probs, num_samples=1).squeeze(1)
955
+ generated.append(next_tok)
956
+ logits, states = self.step(next_tok, states)
957
+ return torch.stack(generated, dim=1)
958
+
959
+ def forward(
960
+ self,
961
+ idx: torch.Tensor,
962
+ targets: Optional[torch.Tensor] = None,
963
+ reduction: str = "mean",
964
+ ) -> torch.Tensor:
965
+ B, T = idx.shape
966
+ assert T <= self.cfg.seq_len, f"Sequence {T} > max {self.cfg.seq_len}"
967
+ x = self.wte(idx)
968
+ x = F.rms_norm(x, (x.size(-1),))
969
+ if _CPU_HAS_BF16:
970
+ with torch.autocast(device_type="cpu", dtype=torch.bfloat16):
971
+ for block in self.blocks:
972
+ x = block(x)
973
+ else:
974
+ for block in self.blocks:
975
+ x = block(x)
976
+ x = self.ln_out(x)
977
+ if targets is None:
978
+ logits = self.lm_head(x).float()
979
+ logits = 15.0 * torch.tanh(logits / 15.0)
980
+ return logits
981
+ return _chunked_cross_entropy(self.lm_head.weight, x, targets)
982
+
983
+
984
+ def gpt2_small_config(**overrides) -> CPUGPTConfig:
985
+ cfg = CPUGPTConfig(
986
+ vocab_size=50257,
987
+ seq_len=1024,
988
+ n_layer=12,
989
+ n_embd=768,
990
+ n_head=12,
991
+ fno_modes=256,
992
+ gla_chunk=64,
993
+ ffn_hidden=2048,
994
+ layer_pattern="SSSL",
995
+ )
996
+ for k, v in overrides.items():
997
+ setattr(cfg, k, v)
998
+ return cfg
999
+
1000
+
1001
+ def smoke_config(**overrides) -> CPUGPTConfig:
1002
+ cfg = CPUGPTConfig(
1003
+ vocab_size=50257,
1004
+ seq_len=256,
1005
+ n_layer=4,
1006
+ n_embd=256,
1007
+ n_head=4,
1008
+ fno_modes=64,
1009
+ gla_chunk=64,
1010
+ ffn_hidden=512,
1011
+ layer_pattern="SSSL",
1012
+ )
1013
+ for k, v in overrides.items():
1014
+ setattr(cfg, k, v)
1015
+ return cfg
1016
+
1017
+
1018
+ def gpt2_8b_config() -> CPUGPTConfig:
1019
+ return CPUGPTConfig(
1020
+ n_layer=32,
1021
+ n_embd=4096,
1022
+ n_head=32,
1023
+ ffn_hidden=14336,
1024
+ fno_modes=512,
1025
+ gla_chunk=256,
1026
+ seq_len=2048,
1027
+ layer_pattern="SSSL",
1028
+ )
1029
+
1030
+
1031
+ def gpt2_1b_config() -> CPUGPTConfig:
1032
+ return CPUGPTConfig(
1033
+ n_layer=24,
1034
+ n_embd=2048,
1035
+ n_head=16,
1036
+ ffn_hidden=5632,
1037
+ fno_modes=512,
1038
+ gla_chunk=256,
1039
+ seq_len=2048,
1040
+ layer_pattern="SSSL",
1041
+ vocab_size=50257,
1042
+ )
1043
+
1044
+
1045
+ def byte125m_config(**overrides) -> CPUGPTConfig:
1046
+ cfg = CPUGPTConfig(
1047
+ vocab_size=261,
1048
+ seq_len=16384,
1049
+ n_layer=12,
1050
+ n_embd=1024,
1051
+ n_head=16,
1052
+ fno_modes=256,
1053
+ gla_chunk=512,
1054
+ ffn_hidden=2816,
1055
+ layer_pattern="SSSL",
1056
+ fft_dtype="fp32",
1057
+ use_flash_fft=True,
1058
+ )
1059
+ for k, v in overrides.items():
1060
+ setattr(cfg, k, v)
1061
+ return cfg
1062
+
1063
+
1064
+ def gpt2_1b_optimized_config() -> CPUGPTConfig:
1065
+ return CPUGPTConfig(
1066
+ n_layer=24,
1067
+ n_embd=2048,
1068
+ n_head=16,
1069
+ ffn_hidden=5632,
1070
+ fno_modes=512,
1071
+ gla_chunk=512,
1072
+ seq_len=2048,
1073
+ layer_pattern="SSSL",
1074
+ vocab_size=50257,
1075
+ fft_dtype="bf16",
1076
+ use_flash_fft=True,
1077
+ )
1078
+
1079
+
1080
+ QWEN_CODER_VOCAB = 152064
1081
+ QWEN3_4B_VOCAB = 151936
1082
+
1083
+
1084
+ def code_3b_config(**overrides) -> CPUGPTConfig:
1085
+ cfg = CPUGPTConfig(
1086
+ vocab_size=QWEN_CODER_VOCAB,
1087
+ seq_len=4096,
1088
+ n_layer=28,
1089
+ n_embd=3072,
1090
+ n_head=24,
1091
+ fno_modes=512,
1092
+ gla_chunk=256,
1093
+ ffn_hidden=8192,
1094
+ layer_pattern="SSSL",
1095
+ gla_delta=True,
1096
+ sliding_window=0,
1097
+ )
1098
+ for k, v in overrides.items():
1099
+ setattr(cfg, k, v)
1100
+ return cfg
1101
+
1102
+
1103
+ def code_3b_exact_config(**overrides) -> CPUGPTConfig:
1104
+ cfg = CPUGPTConfig(
1105
+ vocab_size=QWEN_CODER_VOCAB,
1106
+ seq_len=4096,
1107
+ n_layer=28,
1108
+ n_embd=3584,
1109
+ n_head=28,
1110
+ fno_modes=512,
1111
+ gla_chunk=256,
1112
+ ffn_hidden=18944,
1113
+ layer_pattern="SSSL",
1114
+ gla_delta=True,
1115
+ sliding_window=0,
1116
+ )
1117
+ for k, v in overrides.items():
1118
+ setattr(cfg, k, v)
1119
+ return cfg
1120
+
1121
+
1122
+ def code_4b_config(**overrides) -> CPUGPTConfig:
1123
+ cfg = CPUGPTConfig(
1124
+ vocab_size=QWEN3_4B_VOCAB,
1125
+ seq_len=4096,
1126
+ n_layer=36,
1127
+ n_embd=2560,
1128
+ n_head=20,
1129
+ fno_modes=512,
1130
+ gla_chunk=256,
1131
+ ffn_hidden=9728,
1132
+ layer_pattern="SSSL",
1133
+ gla_delta=True,
1134
+ sliding_window=0,
1135
+ )
1136
+ for k, v in overrides.items():
1137
+ setattr(cfg, k, v)
1138
+ return cfg
1139
+
1140
+
1141
+ def code_1b_config(**overrides) -> CPUGPTConfig:
1142
+ cfg = CPUGPTConfig(
1143
+ vocab_size=QWEN_CODER_VOCAB,
1144
+ seq_len=4096,
1145
+ n_layer=24,
1146
+ n_embd=2048,
1147
+ n_head=16,
1148
+ fno_modes=512,
1149
+ gla_chunk=256,
1150
+ ffn_hidden=5632,
1151
+ layer_pattern="SSSL",
1152
+ gla_delta=True,
1153
+ sliding_window=0,
1154
+ )
1155
+ for k, v in overrides.items():
1156
+ setattr(cfg, k, v)
1157
+ return cfg
1158
+
1159
+
1160
+ def code_1p5b_exact_config(**overrides) -> CPUGPTConfig:
1161
+ cfg = CPUGPTConfig(
1162
+ vocab_size=151936,
1163
+ seq_len=4096,
1164
+ n_layer=28,
1165
+ n_embd=1536,
1166
+ n_head=12,
1167
+ fno_modes=512,
1168
+ gla_chunk=256,
1169
+ ffn_hidden=8960,
1170
+ layer_pattern="SSSL",
1171
+ gla_delta=True,
1172
+ sliding_window=0,
1173
+ )
1174
+ for k, v in overrides.items():
1175
+ setattr(cfg, k, v)
1176
+ return cfg
1177
+
1178
+
1179
+ def arc_a_swa512_config(**overrides) -> CPUGPTConfig:
1180
+ cfg = code_3b_config(swa_window=512)
1181
+ for k, v in overrides.items():
1182
+ setattr(cfg, k, v)
1183
+ return cfg
1184
+
1185
+
1186
+ def arc_c_samba_k6_config(**overrides) -> CPUGPTConfig:
1187
+ cfg = code_3b_config(attn_layer_every=6, attn_full=False, attn_window=512)
1188
+ for k, v in overrides.items():
1189
+ setattr(cfg, k, v)
1190
+ return cfg
1191
+
1192
+
1193
+ def arc_c_samba_k3_config(**overrides) -> CPUGPTConfig:
1194
+ cfg = code_3b_config(attn_layer_every=3, attn_full=False, attn_window=512)
1195
+ for k, v in overrides.items():
1196
+ setattr(cfg, k, v)
1197
+ return cfg
1198
+
1199
+
1200
+ def arc_ub_full4_config(**overrides) -> CPUGPTConfig:
1201
+ cfg = code_3b_config(attn_layer_every=7, attn_full=True)
1202
+ for k, v in overrides.items():
1203
+ setattr(cfg, k, v)
1204
+ return cfg
1205
+
1206
+
1207
+ def arc_d_bigstate_config(**overrides) -> CPUGPTConfig:
1208
+ cfg = code_3b_config(gdn_expand_v=2.0)
1209
+ for k, v in overrides.items():
1210
+ setattr(cfg, k, v)
1211
+ return cfg
1212
+
1213
+
1214
+ def arc_swadelta_config(**overrides) -> CPUGPTConfig:
1215
+ cfg = code_3b_config(swa_fused_window=512)
1216
+ for k, v in overrides.items():
1217
+ setattr(cfg, k, v)
1218
+ return cfg
1219
+
1220
+
1221
+ def code_smoke_config(**overrides) -> CPUGPTConfig:
1222
+ cfg = CPUGPTConfig(
1223
+ vocab_size=QWEN_CODER_VOCAB,
1224
+ seq_len=256,
1225
+ n_layer=4,
1226
+ n_embd=256,
1227
+ n_head=4,
1228
+ fno_modes=64,
1229
+ gla_chunk=64,
1230
+ ffn_hidden=512,
1231
+ layer_pattern="SSSL",
1232
+ gla_delta=False,
1233
+ )
1234
+ for k, v in overrides.items():
1235
+ setattr(cfg, k, v)
1236
+ return cfg
1237
+
1238
+
1239
+ def get_config(name: str) -> CPUGPTConfig:
1240
+ configs = {
1241
+ "gpt2-small": gpt2_small_config,
1242
+ "smoke": smoke_config,
1243
+ "gpt2-8b": gpt2_8b_config,
1244
+ "gpt2-1b": gpt2_1b_config,
1245
+ "gpt2-1b-optimized": gpt2_1b_optimized_config,
1246
+ "byte-125m": byte125m_config,
1247
+ "code-3b": code_3b_config,
1248
+ "code-3b-exact": code_3b_exact_config,
1249
+ "code-4b": code_4b_config,
1250
+ "code-1b": code_1b_config,
1251
+ "code-1.5b-exact": code_1p5b_exact_config,
1252
+ "code-smoke": code_smoke_config,
1253
+ "arc-a-swa512": arc_a_swa512_config,
1254
+ "arc-swadelta": arc_swadelta_config,
1255
+ "arc-c-samba-k6": arc_c_samba_k6_config,
1256
+ "arc-c-samba-k3": arc_c_samba_k3_config,
1257
+ "arc-ub-full4": arc_ub_full4_config,
1258
+ "arc-d-bigstate": arc_d_bigstate_config,
1259
+ }
1260
+ if name not in configs:
1261
+ raise ValueError(f"Unknown config '{name}'. Available: {list(configs.keys())}")
1262
+ return configs[name]()
model_int8.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ba877fe6d1bf500906eef6b7cbbd1fdc10127af8b5286b37d228db94314b2a8b
3
+ size 2081695032
modeling.py ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ import json
3
+ import os
4
+ import time
5
+ from typing import Optional
6
+
7
+ os.environ.setdefault("CUDA_VISIBLE_DEVICES", "")
8
+ os.environ.setdefault("HIP_VISIBLE_DEVICES", "")
9
+ import torch
10
+ import torch.nn as nn
11
+
12
+
13
+ def _read_config(weights_dir: str) -> dict:
14
+ path = os.path.join(weights_dir, "config.json")
15
+ if not os.path.exists(path):
16
+ path = weights_dir if weights_dir.endswith(".json") else path
17
+ with open(path) as f:
18
+ return json.load(f)
19
+
20
+
21
+ def _build_config(cfg_json: dict):
22
+ from model_cpu_gpt2 import CPUGPTConfig
23
+
24
+ return CPUGPTConfig(
25
+ vocab_size=cfg_json["vocab_size"],
26
+ seq_len=cfg_json.get("seq_len", 1024),
27
+ n_layer=cfg_json["n_layer"],
28
+ n_embd=cfg_json["n_embd"],
29
+ n_head=cfg_json["n_head"],
30
+ ffn_hidden=cfg_json["ffn_hidden"],
31
+ layer_pattern=cfg_json.get("layer_pattern", "SSSL"),
32
+ gla_delta=cfg_json.get("gla_delta", True),
33
+ fno_modes=cfg_json.get("fno_modes", 512),
34
+ gla_chunk=cfg_json.get("gla_chunk", 64),
35
+ landmark_layer_every=cfg_json.get("landmark_layer_every", 0),
36
+ landmark_chunk=cfg_json.get("landmark_chunk", 32),
37
+ landmark_max=cfg_json.get("landmark_max", 64),
38
+ attn_layer_every=cfg_json.get("attn_layer_every", 0),
39
+ dropout=0.0,
40
+ )
41
+
42
+
43
+ class FelaLM:
44
+ def __init__(self, model, cfg, tokenizer, cfg_json):
45
+ self.model = model
46
+ self.cfg = cfg
47
+ self.tok = tokenizer
48
+ self.cfg_json = cfg_json
49
+
50
+ def _tid(name):
51
+ i = tokenizer.token_to_id(name)
52
+ return i if i is not None and i >= 0 else None
53
+
54
+ self.fim_prefix = _tid("<|fim_prefix|>")
55
+ self.fim_suffix = _tid("<|fim_suffix|>")
56
+ self.fim_middle = _tid("<|fim_middle|>")
57
+ self.fim_pad = _tid("<|fim_pad|>")
58
+ self.eot = _tid("<|endoftext|>")
59
+ self.fim_ok = None not in (self.fim_prefix, self.fim_suffix, self.fim_middle)
60
+ self._stops = {
61
+ t
62
+ for t in (
63
+ self.fim_prefix,
64
+ self.fim_suffix,
65
+ self.fim_middle,
66
+ self.fim_pad,
67
+ self.eot,
68
+ )
69
+ if t is not None
70
+ }
71
+
72
+ @torch.no_grad()
73
+ def complete(
74
+ self,
75
+ prefix: str,
76
+ suffix: str = "",
77
+ max_tokens: int = 40,
78
+ temperature: float = 0.0,
79
+ single_line: bool = True,
80
+ ) -> dict:
81
+ prefix = prefix or ""
82
+ suffix = suffix or ""
83
+ used_fim = bool(suffix.strip()) and self.fim_ok
84
+ if used_fim:
85
+ ids = (
86
+ [self.fim_prefix]
87
+ + self.tok.encode(prefix).ids
88
+ + [self.fim_suffix]
89
+ + self.tok.encode(suffix).ids
90
+ + [self.fim_middle]
91
+ )
92
+ else:
93
+ ids = self.tok.encode(prefix).ids
94
+ if not ids:
95
+ ids = [self.eot] if self.eot is not None else [0]
96
+ t0 = time.perf_counter()
97
+ states = self.model.init_state(batch_size=1)
98
+ logits = None
99
+ for tok_id in ids:
100
+ logits, states = self.model.step(
101
+ torch.tensor([tok_id], dtype=torch.long), states
102
+ )
103
+ prefill_ms = (time.perf_counter() - t0) * 1000.0
104
+ out_ids = []
105
+ td = time.perf_counter()
106
+ for _ in range(max_tokens):
107
+ if temperature and temperature > 0:
108
+ probs = torch.softmax(logits.float().reshape(-1) / temperature, -1)
109
+ nxt = int(torch.multinomial(probs, 1).item())
110
+ else:
111
+ nxt = int(logits.float().reshape(-1).argmax().item())
112
+ if nxt in self._stops:
113
+ break
114
+ out_ids.append(nxt)
115
+ piece = self.tok.decode(out_ids)
116
+ if single_line and "\n" in piece:
117
+ break
118
+ logits, states = self.model.step(
119
+ torch.tensor([nxt], dtype=torch.long), states
120
+ )
121
+ decode_ms = (time.perf_counter() - td) * 1000.0
122
+ text = self.tok.decode(out_ids) if out_ids else ""
123
+ if single_line:
124
+ text = text.split("\n", 1)[0]
125
+ n = len(out_ids)
126
+ return {
127
+ "middle": text,
128
+ "n_tokens": n,
129
+ "used_fim": used_fim,
130
+ "prompt_tokens": len(ids),
131
+ "prefill_ms": round(prefill_ms, 1),
132
+ "decode_ms": round(decode_ms, 1),
133
+ "tok_per_s": round(n / (decode_ms / 1000.0), 2)
134
+ if decode_ms > 0 and n
135
+ else 0.0,
136
+ }
137
+
138
+
139
+ def _read_bf16_state(weights_dir: str) -> dict:
140
+ from safetensors import safe_open
141
+
142
+ st = {}
143
+ path = os.path.join(weights_dir, "model.safetensors")
144
+ with safe_open(path, framework="pt", device="cpu") as f:
145
+ for k in f.keys():
146
+ st[k] = f.get_tensor(k).float()
147
+ return st
148
+
149
+
150
+ def _read_int8_state(weights_dir: str) -> dict:
151
+ from safetensors import safe_open
152
+
153
+ st = {}
154
+ path = os.path.join(weights_dir, "model_int8.safetensors")
155
+ with safe_open(path, framework="pt", device="cpu") as f:
156
+ keys = list(f.keys())
157
+ for k in keys:
158
+ if k.startswith("keep."):
159
+ st[k[len("keep.") :]] = f.get_tensor(k).float()
160
+ for k in keys:
161
+ if k.startswith("int8."):
162
+ base = k[len("int8.") :]
163
+ w = f.get_tensor(k).float()
164
+ s = f.get_tensor("scale." + base).float()
165
+ st[base] = w * s.reshape([-1] + [1] * (w.dim() - 1))
166
+ return st
167
+
168
+
169
+ def _apply_state(model, st: dict) -> None:
170
+ params = dict(model.named_parameters())
171
+ params.update(dict(model.named_buffers()))
172
+ keys = set(st)
173
+ for k in keys:
174
+ dst = params.get(k)
175
+ if dst is None:
176
+ raise KeyError(f"Checkpoint key {k!r} has no home in the model")
177
+ with torch.no_grad():
178
+ dst.copy_(st[k])
179
+ missing = set(params) - keys
180
+ if missing:
181
+ raise KeyError(f"Missing {len(missing)} params, e.g. {sorted(missing)[:5]}")
182
+
183
+
184
+ def _resolve_quant(weights_dir: str, quant: str) -> str:
185
+ if quant == "auto":
186
+ has_int8 = os.path.exists(os.path.join(weights_dir, "model_int8.safetensors"))
187
+ has_bf16 = os.path.exists(os.path.join(weights_dir, "model.safetensors"))
188
+ return "bf16" if has_bf16 else ("int8" if has_int8 else "bf16")
189
+ return quant
190
+
191
+
192
+ def load_model(
193
+ weights_dir: str = ".", threads: Optional[int] = None, quant: str = "bf16"
194
+ ) -> FelaLM:
195
+ from model_cpu_gpt2 import CPUGPT
196
+ from cpu_patch import enable_cpu_delta
197
+ from tokenizers import Tokenizer
198
+
199
+ if threads:
200
+ torch.set_num_threads(threads)
201
+ if weights_dir.endswith(".safetensors"):
202
+ weights_dir = os.path.dirname(os.path.abspath(weights_dir)) or "."
203
+ cfg_json = _read_config(weights_dir)
204
+ cfg = _build_config(cfg_json)
205
+ model = CPUGPT(cfg)
206
+ model.lm_head = nn.Linear(cfg.n_embd, cfg.vocab_size, bias=False)
207
+ quant = _resolve_quant(weights_dir, quant)
208
+ if quant == "int8":
209
+ st = _read_int8_state(weights_dir)
210
+ else:
211
+ st = _read_bf16_state(weights_dir)
212
+ _apply_state(model, st)
213
+ model.eval()
214
+ enable_cpu_delta(model)
215
+ model.prepare_inference()
216
+ tok_path = os.path.join(weights_dir, "tokenizer.json")
217
+ tokenizer = Tokenizer.from_file(tok_path)
218
+ return FelaLM(model, cfg, tokenizer, cfg_json)
219
+
220
+
221
+ def from_pretrained(repo_id: str = "lowdown-labs/FELA-autocomplete") -> FelaLM:
222
+ from huggingface_hub import hf_hub_download
223
+
224
+ d = os.path.dirname(hf_hub_download(repo_id, "config.json"))
225
+ hf_hub_download(repo_id, "model.safetensors")
226
+ hf_hub_download(repo_id, "tokenizer.json")
227
+ hf_hub_download(repo_id, "model_cpu_gpt2.py")
228
+ for f in ("cpu_delta.py", "cpu_landmark.py", "cpu_swa.py", "cpu_patch.py"):
229
+ hf_hub_download(repo_id, f)
230
+ return load_model(d)
modeling_fela.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+
4
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
5
+ import torch
6
+ import torch.nn as nn
7
+ from transformers import PreTrainedModel
8
+ from transformers.modeling_outputs import CausalLMOutput
9
+
10
+ from .configuration_fela import FelaConfig
11
+ from .cpu_delta import CPUGatedDeltaNet as _cd
12
+ from .cpu_landmark import CPULandmark as _cl
13
+ from .cpu_swa import CPUSlidingWindow as _cs
14
+ from .model_cpu_gpt2 import CPUGPT, CPUGPTConfig
15
+ from .cpu_patch import enable_cpu_delta
16
+
17
+ _KEEP = (_cd, _cl, _cs)
18
+
19
+
20
+ class FelaForCausalLM(PreTrainedModel):
21
+ config_class = FelaConfig
22
+ base_model_prefix = "model"
23
+ _tied_weights_keys = []
24
+ _no_split_modules = []
25
+
26
+ def __init__(self, config):
27
+ super().__init__(config)
28
+ cfg = CPUGPTConfig(
29
+ vocab_size=config.vocab_size,
30
+ seq_len=config.seq_len,
31
+ n_layer=config.n_layer,
32
+ n_embd=config.n_embd,
33
+ n_head=config.n_head,
34
+ ffn_hidden=config.ffn_hidden,
35
+ layer_pattern=config.layer_pattern,
36
+ gla_delta=config.gla_delta,
37
+ fno_modes=config.fno_modes,
38
+ gla_chunk=config.gla_chunk,
39
+ landmark_layer_every=config.landmark_layer_every,
40
+ landmark_chunk=config.landmark_chunk,
41
+ landmark_max=config.landmark_max,
42
+ attn_layer_every=config.attn_layer_every,
43
+ dropout=0.0,
44
+ )
45
+ self.model = CPUGPT(cfg)
46
+ self.model.lm_head = nn.Linear(config.n_embd, config.vocab_size, bias=False)
47
+ self._prepared = False
48
+ self.post_init()
49
+
50
+ def get_input_embeddings(self):
51
+ return self.model.wte
52
+
53
+ def set_input_embeddings(self, value):
54
+ self.model.wte = value
55
+
56
+ def get_output_embeddings(self):
57
+ return self.model.lm_head
58
+
59
+ def set_output_embeddings(self, value):
60
+ self.model.lm_head = value
61
+
62
+ def _ensure_prepared(self):
63
+ if not self._prepared:
64
+ enable_cpu_delta(self.model)
65
+ self.model.prepare_inference()
66
+ self._prepared = True
67
+
68
+ def forward(
69
+ self, input_ids=None, attention_mask=None, labels=None, use_cache=None, **kwargs
70
+ ):
71
+ self._ensure_prepared()
72
+ logits = self.model(input_ids)
73
+ loss = None
74
+ if labels is not None:
75
+ sl = logits[..., :-1, :].contiguous()
76
+ lb = labels[..., 1:].contiguous()
77
+ loss = nn.functional.cross_entropy(sl.view(-1, sl.size(-1)), lb.view(-1))
78
+ return CausalLMOutput(loss=loss, logits=logits)
79
+
80
+ def prepare_inputs_for_generation(self, input_ids, **kwargs):
81
+ return {"input_ids": input_ids}
82
+
83
+ def can_generate(self):
84
+ return True
quickstart/README.md ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Quickstart
2
+
3
+ Load the FELA LLM 1.5 code model and run a real fill in the middle completion on CPU.
4
+ Uses the self contained loader in `../modeling.py`.
5
+
6
+ ## Steps
7
+
8
+ 1. Install the pinned requirements (CPU PyTorch):
9
+
10
+ ```bash
11
+ pip install -r requirements.txt
12
+ ```
13
+
14
+ 2. Point the loader at the directory that holds the weights (this repo). The model
15
+ ships as `model.safetensors` next to `config.json` and `tokenizer.json`:
16
+
17
+ ```bash
18
+ export FELA_LLM_WEIGHTS=/path/to/this/repo
19
+ python run.py
20
+ ```
21
+
22
+ ## Load from Python
23
+
24
+ ```python
25
+ from modeling import load_model
26
+ m = load_model("/path/to/this/repo")
27
+
28
+ # Fill in the middle: give the code before and after the cursor, get the middle
29
+ r = m.complete("def add(a, b):\n ", suffix="\nresult = add(2, 3)\n")
30
+ print(r["middle"]) # e.g. " return a + b"
31
+
32
+ # Plain autocomplete: continue a single line
33
+ print(m.complete("import numpy as ")["middle"]) # e.g. " np"
34
+ ```
35
+
36
+ `complete` is greedy by default, so the same prompt gives the same real output every
37
+ time. When you pass a non empty `suffix` and the tokenizer has the FIM tokens, it uses
38
+ the fill in the middle layout `<|fim_prefix|> P <|fim_suffix|> S <|fim_middle|>` and
39
+ writes the code that belongs between them; otherwise it continues the prefix.
40
+
41
+ ## What to expect
42
+
43
+ This is the final fill in the middle model. Single line patterns land well (imports,
44
+ obvious returns, boilerplate); multi line blocks and novel logic are outside what it is
45
+ built for. Every completion is a genuine forward of the model, never a lookup. The
46
+ reference load path here runs on CPU with no GPU. For fast production serving the same
47
+ weights run int8 on the CPU native FELA server.
quickstart/requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ torch==2.8.0
2
+ safetensors==0.5.3
3
+ tokenizers==0.22.2
4
+ huggingface-hub==0.34.4
5
+ flash-linear-attention==0.5.1
quickstart/run.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import os
3
+ import sys
4
+ import torch
5
+
6
+ sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
7
+ from modeling import load_model
8
+
9
+
10
+ def main():
11
+ ap = argparse.ArgumentParser()
12
+ ap.add_argument(
13
+ "--weights",
14
+ default=os.environ.get("FELA_LLM_WEIGHTS", ".."),
15
+ help="Dir with model.safetensors, config.json, tokenizer.json",
16
+ )
17
+ ap.add_argument("--threads", type=int, default=4)
18
+ args = ap.parse_args()
19
+ torch.set_num_threads(args.threads)
20
+ print("Loading FELA LLM 1.5 on CPU (no GPU needed)...")
21
+ m = load_model(args.weights, threads=args.threads)
22
+ print(
23
+ f"Loaded {m.cfg_json.get('n_params', 0) / 1000000000.0:.2f}B params, fim={m.fim_ok}\n"
24
+ )
25
+ print("Fill in the middle: prefix + suffix -> the model writes the middle")
26
+ prefix = "def add(a, b):\n "
27
+ suffix = "\nresult = add(2, 3)\n"
28
+ r = m.complete(prefix, suffix, max_tokens=16)
29
+ print(f" Prefix: {prefix!r}")
30
+ print(f" Suffix: {suffix!r}")
31
+ print(
32
+ f" Middle: {r['middle']!r} (fim={r['used_fim']}, {r['n_tokens']} tokens, {r['tok_per_s']} tok/s)\n"
33
+ )
34
+ print("Plain autocomplete: continue a single line")
35
+ for prefix in ["import numpy as ", "from fastapi import ", "for i in range("]:
36
+ r = m.complete(prefix, "", max_tokens=12)
37
+ print(f" {prefix!r} -> {r['middle']!r}")
38
+ print(
39
+ "\nThis is the final fill in the middle model. Single line completions land well; multi line blocks and novel logic are outside what it is built for. Every completion is a real forward of the model."
40
+ )
41
+
42
+
43
+ if __name__ == "__main__":
44
+ main()
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ torch==2.8.0
2
+ safetensors==0.5.3
3
+ tokenizers==0.22.2
4
+ huggingface-hub==0.34.4
5
+ flash-linear-attention==0.5.1
space/README.md ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: FELA LLM 1.5 code completion
3
+ colorFrom: indigo
4
+ colorTo: purple
5
+ sdk: gradio
6
+ sdk_version: 4.44.0
7
+ app_file: app.py
8
+ pinned: false
9
+ license: other
10
+ ---
11
+
12
+ # FELA LLM 1.5 code completion
13
+
14
+ Type the code before your cursor (Prefix) and optionally the code after it (Suffix), and
15
+ the model writes the line that goes in between. It runs on a plain CPU with no GPU.
16
+
17
+ ## How it works
18
+
19
+ The model reads your prefix and suffix, and when the suffix is present it uses the fill
20
+ in the middle layout so the completion fits between them. It writes one line at a time,
21
+ greedy by default, so the same prompt gives the same real output every time.
22
+
23
+ ## What to expect
24
+
25
+ This is the final fill in the middle model. Single line patterns land well (imports,
26
+ obvious returns, boilerplate); multi line blocks and novel logic are outside what it is
27
+ built for. Every completion is a genuine forward of the model, never a lookup. For fast
28
+ production serving the same weights run int8 on the CPU native FELA server.
space/app.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import gradio as gr
4
+ import torch
5
+
6
+ sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
7
+ from modeling import load_model
8
+
9
+ WEIGHTS = os.environ.get("FELA_LLM_WEIGHTS", "..")
10
+ try:
11
+ torch.set_num_threads(4)
12
+ MODEL = load_model(WEIGHTS, threads=4)
13
+ except Exception as e:
14
+ MODEL = None
15
+ print(f"[Note] Model not loaded ({e}); the Space reports the input instead.")
16
+
17
+
18
+ def complete(prefix, suffix, max_tokens):
19
+ if MODEL is None:
20
+ return "Weights not found. Set FELA_LLM_WEIGHTS to the repo directory."
21
+ if not (prefix or "").strip() and (not (suffix or "").strip()):
22
+ return "Type some code before the cursor (prefix), and optionally after it (suffix)."
23
+ r = MODEL.complete(prefix or "", suffix or "", max_tokens=int(max_tokens))
24
+ tag = "fill in the middle" if r["used_fim"] else "continuation"
25
+ return f"{r['middle']}\n\n[{tag}, {r['n_tokens']} tokens, {r['tok_per_s']} tok/s on CPU]"
26
+
27
+
28
+ with gr.Blocks(title="FELA LLM 1.5 code completion") as demo:
29
+ gr.Markdown(
30
+ "# FELA LLM 1.5 code completion\nA CPU native code autocomplete model. Type the code before your cursor in Prefix, and optionally the code after your cursor in Suffix, and it writes the line that goes in between. It runs on a plain CPU with no GPU. This is the final fill in the middle model: single line patterns land well, multi line blocks and novel logic are outside what it is built for. Every completion is a real forward of the model."
31
+ )
32
+ prefix = gr.Textbox(
33
+ label="Prefix (code before the cursor)", value="def add(a, b):\n ", lines=4
34
+ )
35
+ suffix = gr.Textbox(
36
+ label="Suffix (code after the cursor, optional)",
37
+ value="\nresult = add(2, 3)\n",
38
+ lines=2,
39
+ )
40
+ max_tokens = gr.Slider(4, 64, value=16, step=1, label="Max tokens")
41
+ out = gr.Textbox(label="Completed middle", lines=4)
42
+ gr.Button("Complete").click(complete, [prefix, suffix, max_tokens], out)
43
+ if __name__ == "__main__":
44
+ demo.launch()
space/requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ gradio==4.44.0
2
+ torch==2.8.0
3
+ safetensors==0.5.3
4
+ tokenizers==0.22.2
5
+ huggingface-hub==0.34.4
6
+ flash-linear-attention==0.5.1
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
verify.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import sys
3
+ import torch
4
+ from modeling import load_model
5
+
6
+ VERIFICATION_TOP5 = [319, 516, 22441, 497, 4102]
7
+
8
+
9
+ def main():
10
+ ap = argparse.ArgumentParser()
11
+ ap.add_argument("--weights", default=".")
12
+ ap.add_argument("--capture", action="store_true")
13
+ args = ap.parse_args()
14
+ torch.set_num_threads(1)
15
+ m = load_model(args.weights, threads=1)
16
+ ids = torch.arange(16, dtype=torch.long).unsqueeze(0) + 100
17
+ with torch.no_grad():
18
+ logits = m.model(ids)
19
+ assert logits.dim() == 3 and logits.size(-1) == m.cfg.vocab_size, (
20
+ f"Bad output shape {tuple(logits.shape)} (expected vocab {m.cfg.vocab_size})"
21
+ )
22
+ assert torch.isfinite(logits).all(), "Non finite logits"
23
+ print(f"Output shape ok {tuple(logits.shape)}")
24
+ top5 = logits[0, -1].topk(5).indices.tolist()
25
+ if args.capture:
26
+ print(f"VERIFICATION_TOP5 = {top5}")
27
+ return
28
+ if top5 != VERIFICATION_TOP5:
29
+ print(
30
+ f"[WARNING] top 5 tokens {top5} differ from the reference {VERIFICATION_TOP5}; small CPU numeric differences can move ties across machines"
31
+ )
32
+ else:
33
+ print("Verification value ok")
34
+ r = m.complete("import numpy as ", "", max_tokens=4)
35
+ print(f"Sample completion: 'import numpy as ' -> {r['middle']!r}")
36
+
37
+
38
+ if __name__ == "__main__":
39
+ main()