OpceanAI commited on
Commit
5b8649d
·
verified ·
1 Parent(s): 29eafb6

Paper as a print-quality PDF (10 pages), corrected figures 5 and 8, PDF builder

Browse files
.gitattributes CHANGED
@@ -35,3 +35,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  tokenizer.json filter=lfs diff=lfs merge=lfs -text
37
  tokenizer_config.json filter=lfs diff=lfs merge=lfs -text
 
 
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
  tokenizer.json filter=lfs diff=lfs merge=lfs -text
37
  tokenizer_config.json filter=lfs diff=lfs merge=lfs -text
38
+ PAPER-tinyllama-rv32.pdf filter=lfs diff=lfs merge=lfs -text
PAPER-tinyllama-rv32.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7049f5288bb997e7e8fc2a9809a4c6787788c94ae2045c2b330fee1be498057f
3
+ size 275142
README.md CHANGED
@@ -169,11 +169,12 @@ instruction.
169
 
170
  | Path | What |
171
  |---|---|
172
- | `PAPER-tinyllama-rv32.md` | the paper: method, verification, the 40/40 question, the merge, the limits |
 
173
  | `DESIGN-tinyllama.md` | the working design log with every wall and its fix |
174
  | `figures/` | instruction mix, quantization cost, 40-token agreement, speed, storage |
175
  | `logs/` | five circuit-vs-reference checkpoints, the G4 run, the 40-token run, a kernel dump |
176
- | `scripts/` | kernel (`tl.c`), assembly matvec (`vd2.S`), exporter, Python mirror, circuit checker, G4 driver |
177
  | `results.json` | raw result document of the G4 run |
178
  | `colab_bundle.zip` | the patched tree and scripts, ready for a Colab VM |
179
 
 
169
 
170
  | Path | What |
171
  |---|---|
172
+ | `PAPER-tinyllama-rv32.pdf` | the paper as a print-quality PDF, 10 pages, figures and tables typeset |
173
+ | `PAPER-tinyllama-rv32.md` | the paper source: method, verification, the 40/40 question, the merge, the limits |
174
  | `DESIGN-tinyllama.md` | the working design log with every wall and its fix |
175
  | `figures/` | instruction mix, quantization cost, 40-token agreement, speed, storage |
176
  | `logs/` | five circuit-vs-reference checkpoints, the G4 run, the 40-token run, a kernel dump |
177
+ | `scripts/` | kernel (`tl.c`), assembly matvec (`vd2.S`), exporter, Python mirror, circuit checker, G4 driver, PDF builder |
178
  | `results.json` | raw result document of the G4 run |
179
  | `colab_bundle.zip` | the patched tree and scripts, ready for a Colab VM |
180
 
fig5_instruction_mix.png ADDED
fig8_speed.png ADDED
figures/fig5_instruction_mix.png CHANGED
figures/fig8_speed.png CHANGED
make_paper_pdf.py ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Build a print-quality PDF from PAPER-tinyllama-rv32.md.
2
+
3
+ python make_paper_pdf.py [PAPER.md] [OUT.pdf]
4
+
5
+ Renders headings, paragraphs, bullets, tables, fenced code, figures and captions on A4 with
6
+ running heads, page numbers and a title block. No LaTeX, no browser.
7
+ """
8
+ import os
9
+ import re
10
+ import sys
11
+
12
+ from reportlab.lib import colors
13
+ from reportlab.lib.enums import TA_CENTER, TA_JUSTIFY, TA_LEFT
14
+ from reportlab.lib.pagesizes import A4
15
+ from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
16
+ from reportlab.lib.units import mm
17
+ from reportlab.platypus import (BaseDocTemplate, Frame, Image, KeepTogether, PageBreak,
18
+ PageTemplate, Paragraph, Spacer, Table, TableStyle)
19
+
20
+ SRC = sys.argv[1] if len(sys.argv) > 1 else "PAPER-tinyllama-rv32.md"
21
+ OUT = sys.argv[2] if len(sys.argv) > 2 else "PAPER-tinyllama-rv32.pdf"
22
+ BASE = os.path.dirname(os.path.abspath(SRC))
23
+ INK = colors.HexColor("#111111")
24
+ MUTED = colors.HexColor("#555555")
25
+ RULE = colors.HexColor("#bbbbbb")
26
+ ACCENT = colors.HexColor("#1f3a5f")
27
+ CODEBG = colors.HexColor("#f4f5f7")
28
+
29
+ ss = getSampleStyleSheet()
30
+ TITLE = ParagraphStyle("t", parent=ss["Title"], fontName="Times-Bold", fontSize=21, leading=25,
31
+ textColor=INK, spaceAfter=6, alignment=TA_LEFT)
32
+ SUBTITLE = ParagraphStyle("st", parent=ss["Normal"], fontName="Times-Italic", fontSize=11.5,
33
+ leading=15, textColor=MUTED, spaceAfter=2)
34
+ H1 = ParagraphStyle("h1", parent=ss["Heading1"], fontName="Times-Bold", fontSize=14, leading=18,
35
+ textColor=ACCENT, spaceBefore=14, spaceAfter=6, keepWithNext=1)
36
+ H2 = ParagraphStyle("h2", parent=ss["Heading2"], fontName="Times-Bold", fontSize=11.5, leading=15,
37
+ textColor=INK, spaceBefore=10, spaceAfter=4, keepWithNext=1)
38
+ BODY = ParagraphStyle("b", parent=ss["BodyText"], fontName="Times-Roman", fontSize=10, leading=14.2,
39
+ alignment=TA_JUSTIFY, spaceAfter=5)
40
+ BULLET = ParagraphStyle("bl", parent=BODY, leftIndent=12, bulletIndent=2, spaceAfter=3)
41
+ QUOTE = ParagraphStyle("q", parent=BODY, fontName="Times-Italic", leftIndent=10, rightIndent=6,
42
+ textColor=colors.HexColor("#333333"), spaceBefore=2, spaceAfter=6)
43
+ CODE = ParagraphStyle("c", parent=ss["Code"], fontName="Courier", fontSize=7.4, leading=9.4,
44
+ textColor=INK, backColor=CODEBG, borderPadding=(4, 5, 4, 5),
45
+ leftIndent=6, rightIndent=6, spaceBefore=3, spaceAfter=7)
46
+ CAPTION = ParagraphStyle("cap", parent=ss["Normal"], fontName="Times-Italic", fontSize=8.4,
47
+ leading=11, textColor=MUTED, alignment=TA_CENTER, spaceBefore=3,
48
+ spaceAfter=9)
49
+ CELL = ParagraphStyle("cell", parent=ss["Normal"], fontName="Times-Roman", fontSize=8.2,
50
+ leading=10.4, textColor=INK)
51
+ CELLH = ParagraphStyle("cellh", parent=CELL, fontName="Times-Bold")
52
+ CELLR = ParagraphStyle("cellr", parent=CELL, alignment=2)
53
+
54
+
55
+ def inline(text):
56
+ text = text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
57
+ text = re.sub(r"`([^`]+)`", r'<font name="Courier" size="8.6">\1</font>', text)
58
+ text = re.sub(r"\*\*([^*]+)\*\*", r"<b>\1</b>", text)
59
+ text = re.sub(r"(?<!\*)\*([^*]+)\*(?!\*)", r"<i>\1</i>", text)
60
+ text = re.sub(r"\[([^\]]+)\]\(([^)]+)\)", r'<font color="#1f3a5f">\1</font>', text)
61
+ return text
62
+
63
+
64
+ def is_number(cell):
65
+ c = cell.strip().replace(",", "").replace("%", "").replace("**", "")
66
+ try:
67
+ float(c)
68
+ return True
69
+ except ValueError:
70
+ return False
71
+
72
+
73
+ def table_from(rows):
74
+ has_head = len(rows) > 1 and not all(set(c) <= set("-: ") for c in rows[1])
75
+ head = rows[0] if has_head else None
76
+ body = rows[1:] if has_head else rows
77
+ data = []
78
+ if head:
79
+ data.append([Paragraph(inline(c), CELLH) for c in head])
80
+ for r in body:
81
+ data.append([Paragraph(inline(c), CELLR if is_number(c) and i else CELL)
82
+ for i, c in enumerate(r)])
83
+ ncol = max(len(r) for r in data)
84
+ for r in data:
85
+ while len(r) < ncol:
86
+ r.append(Paragraph("", CELL))
87
+ width = 170 * mm
88
+ first = 58 * mm if ncol > 2 else width / ncol
89
+ cols = [first] + [(width - first) / (ncol - 1)] * (ncol - 1) if ncol > 1 else [width]
90
+ t = Table(data, colWidths=cols, hAlign="LEFT", repeatRows=1 if head else 0)
91
+ style = [("VALIGN", (0, 0), (-1, -1), "TOP"),
92
+ ("TOPPADDING", (0, 0), (-1, -1), 3),
93
+ ("BOTTOMPADDING", (0, 0), (-1, -1), 3),
94
+ ("LEFTPADDING", (0, 0), (-1, -1), 5),
95
+ ("RIGHTPADDING", (0, 0), (-1, -1), 5),
96
+ ("LINEBELOW", (0, 0), (-1, -1), 0.25, RULE)]
97
+ if head:
98
+ style += [("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#eceff3")),
99
+ ("LINEABOVE", (0, 0), (-1, 0), 0.9, ACCENT),
100
+ ("LINEBELOW", (0, 0), (-1, 0), 0.6, ACCENT)]
101
+ t.setStyle(TableStyle(style))
102
+ return t
103
+
104
+
105
+ def figure(path):
106
+ full = os.path.join(BASE, path)
107
+ if not os.path.exists(full):
108
+ return None
109
+ from PIL import Image as PILImage
110
+ w, h = PILImage.open(full).size
111
+ width = min(150 * mm, w * 0.75)
112
+ return Image(full, width=width, height=width * h / w)
113
+
114
+
115
+ def parse(md):
116
+ flow = []
117
+ lines = md.splitlines()
118
+ i = 0
119
+ in_code = False
120
+ code_buf = []
121
+ title_done = False
122
+ para = []
123
+ bullets = []
124
+ table = []
125
+
126
+ def flush_para():
127
+ nonlocal para
128
+ if para:
129
+ flow.append(Paragraph(inline(" ".join(para)), BODY))
130
+ para = []
131
+
132
+ def flush_bullets():
133
+ nonlocal bullets
134
+ for b in bullets:
135
+ flow.append(Paragraph(inline(b), BULLET, bulletText="\u2022"))
136
+ if bullets:
137
+ flow.append(Spacer(1, 2))
138
+ bullets = []
139
+
140
+ def flush_table():
141
+ nonlocal table
142
+ if table:
143
+ flow.append(table_from(table))
144
+ flow.append(Spacer(1, 7))
145
+ table = []
146
+
147
+ while i < len(lines):
148
+ ln = lines[i]
149
+ stripped = ln.strip()
150
+
151
+ if stripped.startswith("```"):
152
+ flush_para(); flush_bullets(); flush_table()
153
+ if in_code:
154
+ flow.append(Paragraph("<br/>".join(c.replace(" ", "&nbsp;") for c in code_buf)
155
+ .replace("&nbsp;&nbsp;&nbsp;&nbsp;", "&nbsp;&nbsp;&nbsp;&nbsp;"),
156
+ CODE))
157
+ code_buf = []
158
+ in_code = False
159
+ else:
160
+ in_code = True
161
+ i += 1
162
+ continue
163
+ if in_code:
164
+ code_buf.append(ln if ln else "&nbsp;")
165
+ i += 1
166
+ continue
167
+
168
+ if not stripped:
169
+ flush_para(); flush_bullets(); flush_table()
170
+ i += 1
171
+ continue
172
+
173
+ if stripped.startswith("|"):
174
+ flush_para(); flush_bullets()
175
+ cells = [c.strip() for c in stripped.strip("|").split("|")]
176
+ if not all(set(c) <= set("-: ") for c in cells):
177
+ table.append(cells)
178
+ i += 1
179
+ continue
180
+
181
+ m = re.match(r"^!\[([^\]]*)\]\(([^)]+)\)", stripped)
182
+ if m:
183
+ flush_para(); flush_bullets(); flush_table()
184
+ cap = m.group(1)
185
+ j = i + 1
186
+ while j < len(lines) and not lines[j].strip():
187
+ j += 1
188
+ if j < len(lines):
189
+ nxt = lines[j].strip()
190
+ mm = re.match(r"^\*?(Figure\s+\d+\.[^*]*)\*?$", nxt.lstrip("*_").rstrip("*_"))
191
+ if mm:
192
+ cap = mm.group(1)
193
+ i = j
194
+ img = figure(m.group(2))
195
+ if img:
196
+ flow.append(KeepTogether([img, Paragraph(inline(cap), CAPTION)]))
197
+ i += 1
198
+ continue
199
+
200
+ m = re.match(r"^#\s+(.*)$", stripped)
201
+ if m and not title_done:
202
+ flush_para(); flush_bullets(); flush_table()
203
+ flow.append(Paragraph(inline(m.group(1)), TITLE))
204
+ title_done = True
205
+ i += 1
206
+ continue
207
+
208
+ m = re.match(r"^##\s+(.*)$", stripped)
209
+ if m:
210
+ flush_para(); flush_bullets(); flush_table()
211
+ flow.append(Paragraph(inline(m.group(1)), H1))
212
+ i += 1
213
+ continue
214
+
215
+ m = re.match(r"^###\s+(.*)$", stripped)
216
+ if m:
217
+ flush_para(); flush_bullets(); flush_table()
218
+ flow.append(Paragraph(inline(m.group(1)), H2))
219
+ i += 1
220
+ continue
221
+
222
+ if stripped.startswith(">"):
223
+ flush_para(); flush_bullets(); flush_table()
224
+ flow.append(Paragraph(inline(stripped.lstrip("> ").strip()), QUOTE))
225
+ i += 1
226
+ continue
227
+
228
+ m = re.match(r"^[-*]\s+(.*)$", stripped)
229
+ if m:
230
+ flush_para(); flush_table()
231
+ bullets.append(m.group(1))
232
+ i += 1
233
+ continue
234
+
235
+ m = re.match(r"^\d+\.\s+(.*)$", stripped)
236
+ if m:
237
+ flush_para(); flush_table()
238
+ bullets.append(m.group(1))
239
+ i += 1
240
+ continue
241
+
242
+ if bullets and (ln.startswith(" ") or ln.startswith("\t")):
243
+ bullets[-1] = bullets[-1] + " " + stripped
244
+ i += 1
245
+ continue
246
+
247
+ if stripped in ("---", "***"):
248
+ flush_para(); flush_bullets(); flush_table()
249
+ flow.append(Spacer(1, 6))
250
+ i += 1
251
+ continue
252
+
253
+ if title_done and not para and not flow[-1:] == [None] and \
254
+ getattr(flow[-1], "style", None) is TITLE:
255
+ flow.append(Paragraph(inline(stripped), SUBTITLE))
256
+ i += 1
257
+ continue
258
+
259
+ para.append(stripped)
260
+ i += 1
261
+
262
+ flush_para(); flush_bullets(); flush_table()
263
+ return flow
264
+
265
+
266
+ def decorate(canvas, doc):
267
+ canvas.saveState()
268
+ canvas.setFont("Times-Roman", 8)
269
+ canvas.setFillColor(MUTED)
270
+ if doc.page > 1:
271
+ canvas.drawString(20 * mm, A4[1] - 13 * mm,
272
+ "TinyLLama-v0 as an int4 workload on a token-level RISC-V machine")
273
+ canvas.setStrokeColor(RULE)
274
+ canvas.setLineWidth(0.3)
275
+ canvas.line(20 * mm, A4[1] - 15 * mm, A4[0] - 20 * mm, A4[1] - 15 * mm)
276
+ canvas.drawRightString(A4[0] - 20 * mm, 12 * mm, str(doc.page))
277
+ canvas.restoreState()
278
+
279
+
280
+ def main():
281
+ md = open(SRC, encoding="utf-8").read()
282
+ flow = parse(md)
283
+ doc = BaseDocTemplate(OUT, pagesize=A4, leftMargin=20 * mm, rightMargin=20 * mm,
284
+ topMargin=20 * mm, bottomMargin=18 * mm,
285
+ title="TinyLLama-v0 as an int4 workload on a token-level RISC-V machine",
286
+ author="OpceanAI", subject="int4 inference on a token-level RV32 machine")
287
+ frame = Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height, id="f")
288
+ doc.addPageTemplates([PageTemplate(id="p", frames=[frame], onPage=decorate)])
289
+ doc.build(flow)
290
+ print("escrito", OUT, os.path.getsize(OUT) // 1024, "KB")
291
+
292
+
293
+ if __name__ == "__main__":
294
+ main()
scripts/make_paper_pdf.py ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Build a print-quality PDF from PAPER-tinyllama-rv32.md.
2
+
3
+ python make_paper_pdf.py [PAPER.md] [OUT.pdf]
4
+
5
+ Renders headings, paragraphs, bullets, tables, fenced code, figures and captions on A4 with
6
+ running heads, page numbers and a title block. No LaTeX, no browser.
7
+ """
8
+ import os
9
+ import re
10
+ import sys
11
+
12
+ from reportlab.lib import colors
13
+ from reportlab.lib.enums import TA_CENTER, TA_JUSTIFY, TA_LEFT
14
+ from reportlab.lib.pagesizes import A4
15
+ from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
16
+ from reportlab.lib.units import mm
17
+ from reportlab.platypus import (BaseDocTemplate, Frame, Image, KeepTogether, PageBreak,
18
+ PageTemplate, Paragraph, Spacer, Table, TableStyle)
19
+
20
+ SRC = sys.argv[1] if len(sys.argv) > 1 else "PAPER-tinyllama-rv32.md"
21
+ OUT = sys.argv[2] if len(sys.argv) > 2 else "PAPER-tinyllama-rv32.pdf"
22
+ BASE = os.path.dirname(os.path.abspath(SRC))
23
+ INK = colors.HexColor("#111111")
24
+ MUTED = colors.HexColor("#555555")
25
+ RULE = colors.HexColor("#bbbbbb")
26
+ ACCENT = colors.HexColor("#1f3a5f")
27
+ CODEBG = colors.HexColor("#f4f5f7")
28
+
29
+ ss = getSampleStyleSheet()
30
+ TITLE = ParagraphStyle("t", parent=ss["Title"], fontName="Times-Bold", fontSize=21, leading=25,
31
+ textColor=INK, spaceAfter=6, alignment=TA_LEFT)
32
+ SUBTITLE = ParagraphStyle("st", parent=ss["Normal"], fontName="Times-Italic", fontSize=11.5,
33
+ leading=15, textColor=MUTED, spaceAfter=2)
34
+ H1 = ParagraphStyle("h1", parent=ss["Heading1"], fontName="Times-Bold", fontSize=14, leading=18,
35
+ textColor=ACCENT, spaceBefore=14, spaceAfter=6, keepWithNext=1)
36
+ H2 = ParagraphStyle("h2", parent=ss["Heading2"], fontName="Times-Bold", fontSize=11.5, leading=15,
37
+ textColor=INK, spaceBefore=10, spaceAfter=4, keepWithNext=1)
38
+ BODY = ParagraphStyle("b", parent=ss["BodyText"], fontName="Times-Roman", fontSize=10, leading=14.2,
39
+ alignment=TA_JUSTIFY, spaceAfter=5)
40
+ BULLET = ParagraphStyle("bl", parent=BODY, leftIndent=12, bulletIndent=2, spaceAfter=3)
41
+ QUOTE = ParagraphStyle("q", parent=BODY, fontName="Times-Italic", leftIndent=10, rightIndent=6,
42
+ textColor=colors.HexColor("#333333"), spaceBefore=2, spaceAfter=6)
43
+ CODE = ParagraphStyle("c", parent=ss["Code"], fontName="Courier", fontSize=7.4, leading=9.4,
44
+ textColor=INK, backColor=CODEBG, borderPadding=(4, 5, 4, 5),
45
+ leftIndent=6, rightIndent=6, spaceBefore=3, spaceAfter=7)
46
+ CAPTION = ParagraphStyle("cap", parent=ss["Normal"], fontName="Times-Italic", fontSize=8.4,
47
+ leading=11, textColor=MUTED, alignment=TA_CENTER, spaceBefore=3,
48
+ spaceAfter=9)
49
+ CELL = ParagraphStyle("cell", parent=ss["Normal"], fontName="Times-Roman", fontSize=8.2,
50
+ leading=10.4, textColor=INK)
51
+ CELLH = ParagraphStyle("cellh", parent=CELL, fontName="Times-Bold")
52
+ CELLR = ParagraphStyle("cellr", parent=CELL, alignment=2)
53
+
54
+
55
+ def inline(text):
56
+ text = text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
57
+ text = re.sub(r"`([^`]+)`", r'<font name="Courier" size="8.6">\1</font>', text)
58
+ text = re.sub(r"\*\*([^*]+)\*\*", r"<b>\1</b>", text)
59
+ text = re.sub(r"(?<!\*)\*([^*]+)\*(?!\*)", r"<i>\1</i>", text)
60
+ text = re.sub(r"\[([^\]]+)\]\(([^)]+)\)", r'<font color="#1f3a5f">\1</font>', text)
61
+ return text
62
+
63
+
64
+ def is_number(cell):
65
+ c = cell.strip().replace(",", "").replace("%", "").replace("**", "")
66
+ try:
67
+ float(c)
68
+ return True
69
+ except ValueError:
70
+ return False
71
+
72
+
73
+ def table_from(rows):
74
+ has_head = len(rows) > 1 and not all(set(c) <= set("-: ") for c in rows[1])
75
+ head = rows[0] if has_head else None
76
+ body = rows[1:] if has_head else rows
77
+ data = []
78
+ if head:
79
+ data.append([Paragraph(inline(c), CELLH) for c in head])
80
+ for r in body:
81
+ data.append([Paragraph(inline(c), CELLR if is_number(c) and i else CELL)
82
+ for i, c in enumerate(r)])
83
+ ncol = max(len(r) for r in data)
84
+ for r in data:
85
+ while len(r) < ncol:
86
+ r.append(Paragraph("", CELL))
87
+ width = 170 * mm
88
+ first = 58 * mm if ncol > 2 else width / ncol
89
+ cols = [first] + [(width - first) / (ncol - 1)] * (ncol - 1) if ncol > 1 else [width]
90
+ t = Table(data, colWidths=cols, hAlign="LEFT", repeatRows=1 if head else 0)
91
+ style = [("VALIGN", (0, 0), (-1, -1), "TOP"),
92
+ ("TOPPADDING", (0, 0), (-1, -1), 3),
93
+ ("BOTTOMPADDING", (0, 0), (-1, -1), 3),
94
+ ("LEFTPADDING", (0, 0), (-1, -1), 5),
95
+ ("RIGHTPADDING", (0, 0), (-1, -1), 5),
96
+ ("LINEBELOW", (0, 0), (-1, -1), 0.25, RULE)]
97
+ if head:
98
+ style += [("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#eceff3")),
99
+ ("LINEABOVE", (0, 0), (-1, 0), 0.9, ACCENT),
100
+ ("LINEBELOW", (0, 0), (-1, 0), 0.6, ACCENT)]
101
+ t.setStyle(TableStyle(style))
102
+ return t
103
+
104
+
105
+ def figure(path):
106
+ full = os.path.join(BASE, path)
107
+ if not os.path.exists(full):
108
+ return None
109
+ from PIL import Image as PILImage
110
+ w, h = PILImage.open(full).size
111
+ width = min(150 * mm, w * 0.75)
112
+ return Image(full, width=width, height=width * h / w)
113
+
114
+
115
+ def parse(md):
116
+ flow = []
117
+ lines = md.splitlines()
118
+ i = 0
119
+ in_code = False
120
+ code_buf = []
121
+ title_done = False
122
+ para = []
123
+ bullets = []
124
+ table = []
125
+
126
+ def flush_para():
127
+ nonlocal para
128
+ if para:
129
+ flow.append(Paragraph(inline(" ".join(para)), BODY))
130
+ para = []
131
+
132
+ def flush_bullets():
133
+ nonlocal bullets
134
+ for b in bullets:
135
+ flow.append(Paragraph(inline(b), BULLET, bulletText="\u2022"))
136
+ if bullets:
137
+ flow.append(Spacer(1, 2))
138
+ bullets = []
139
+
140
+ def flush_table():
141
+ nonlocal table
142
+ if table:
143
+ flow.append(table_from(table))
144
+ flow.append(Spacer(1, 7))
145
+ table = []
146
+
147
+ while i < len(lines):
148
+ ln = lines[i]
149
+ stripped = ln.strip()
150
+
151
+ if stripped.startswith("```"):
152
+ flush_para(); flush_bullets(); flush_table()
153
+ if in_code:
154
+ flow.append(Paragraph("<br/>".join(c.replace(" ", "&nbsp;") for c in code_buf)
155
+ .replace("&nbsp;&nbsp;&nbsp;&nbsp;", "&nbsp;&nbsp;&nbsp;&nbsp;"),
156
+ CODE))
157
+ code_buf = []
158
+ in_code = False
159
+ else:
160
+ in_code = True
161
+ i += 1
162
+ continue
163
+ if in_code:
164
+ code_buf.append(ln if ln else "&nbsp;")
165
+ i += 1
166
+ continue
167
+
168
+ if not stripped:
169
+ flush_para(); flush_bullets(); flush_table()
170
+ i += 1
171
+ continue
172
+
173
+ if stripped.startswith("|"):
174
+ flush_para(); flush_bullets()
175
+ cells = [c.strip() for c in stripped.strip("|").split("|")]
176
+ if not all(set(c) <= set("-: ") for c in cells):
177
+ table.append(cells)
178
+ i += 1
179
+ continue
180
+
181
+ m = re.match(r"^!\[([^\]]*)\]\(([^)]+)\)", stripped)
182
+ if m:
183
+ flush_para(); flush_bullets(); flush_table()
184
+ cap = m.group(1)
185
+ j = i + 1
186
+ while j < len(lines) and not lines[j].strip():
187
+ j += 1
188
+ if j < len(lines):
189
+ nxt = lines[j].strip()
190
+ mm = re.match(r"^\*?(Figure\s+\d+\.[^*]*)\*?$", nxt.lstrip("*_").rstrip("*_"))
191
+ if mm:
192
+ cap = mm.group(1)
193
+ i = j
194
+ img = figure(m.group(2))
195
+ if img:
196
+ flow.append(KeepTogether([img, Paragraph(inline(cap), CAPTION)]))
197
+ i += 1
198
+ continue
199
+
200
+ m = re.match(r"^#\s+(.*)$", stripped)
201
+ if m and not title_done:
202
+ flush_para(); flush_bullets(); flush_table()
203
+ flow.append(Paragraph(inline(m.group(1)), TITLE))
204
+ title_done = True
205
+ i += 1
206
+ continue
207
+
208
+ m = re.match(r"^##\s+(.*)$", stripped)
209
+ if m:
210
+ flush_para(); flush_bullets(); flush_table()
211
+ flow.append(Paragraph(inline(m.group(1)), H1))
212
+ i += 1
213
+ continue
214
+
215
+ m = re.match(r"^###\s+(.*)$", stripped)
216
+ if m:
217
+ flush_para(); flush_bullets(); flush_table()
218
+ flow.append(Paragraph(inline(m.group(1)), H2))
219
+ i += 1
220
+ continue
221
+
222
+ if stripped.startswith(">"):
223
+ flush_para(); flush_bullets(); flush_table()
224
+ flow.append(Paragraph(inline(stripped.lstrip("> ").strip()), QUOTE))
225
+ i += 1
226
+ continue
227
+
228
+ m = re.match(r"^[-*]\s+(.*)$", stripped)
229
+ if m:
230
+ flush_para(); flush_table()
231
+ bullets.append(m.group(1))
232
+ i += 1
233
+ continue
234
+
235
+ m = re.match(r"^\d+\.\s+(.*)$", stripped)
236
+ if m:
237
+ flush_para(); flush_table()
238
+ bullets.append(m.group(1))
239
+ i += 1
240
+ continue
241
+
242
+ if bullets and (ln.startswith(" ") or ln.startswith("\t")):
243
+ bullets[-1] = bullets[-1] + " " + stripped
244
+ i += 1
245
+ continue
246
+
247
+ if stripped in ("---", "***"):
248
+ flush_para(); flush_bullets(); flush_table()
249
+ flow.append(Spacer(1, 6))
250
+ i += 1
251
+ continue
252
+
253
+ if title_done and not para and not flow[-1:] == [None] and \
254
+ getattr(flow[-1], "style", None) is TITLE:
255
+ flow.append(Paragraph(inline(stripped), SUBTITLE))
256
+ i += 1
257
+ continue
258
+
259
+ para.append(stripped)
260
+ i += 1
261
+
262
+ flush_para(); flush_bullets(); flush_table()
263
+ return flow
264
+
265
+
266
+ def decorate(canvas, doc):
267
+ canvas.saveState()
268
+ canvas.setFont("Times-Roman", 8)
269
+ canvas.setFillColor(MUTED)
270
+ if doc.page > 1:
271
+ canvas.drawString(20 * mm, A4[1] - 13 * mm,
272
+ "TinyLLama-v0 as an int4 workload on a token-level RISC-V machine")
273
+ canvas.setStrokeColor(RULE)
274
+ canvas.setLineWidth(0.3)
275
+ canvas.line(20 * mm, A4[1] - 15 * mm, A4[0] - 20 * mm, A4[1] - 15 * mm)
276
+ canvas.drawRightString(A4[0] - 20 * mm, 12 * mm, str(doc.page))
277
+ canvas.restoreState()
278
+
279
+
280
+ def main():
281
+ md = open(SRC, encoding="utf-8").read()
282
+ flow = parse(md)
283
+ doc = BaseDocTemplate(OUT, pagesize=A4, leftMargin=20 * mm, rightMargin=20 * mm,
284
+ topMargin=20 * mm, bottomMargin=18 * mm,
285
+ title="TinyLLama-v0 as an int4 workload on a token-level RISC-V machine",
286
+ author="OpceanAI", subject="int4 inference on a token-level RV32 machine")
287
+ frame = Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height, id="f")
288
+ doc.addPageTemplates([PageTemplate(id="p", frames=[frame], onPage=decorate)])
289
+ doc.build(flow)
290
+ print("escrito", OUT, os.path.getsize(OUT) // 1024, "KB")
291
+
292
+
293
+ if __name__ == "__main__":
294
+ main()