rust-pgn-reader-python-binding 4.2.0__cp315-cp315-musllinux_1_2_armv7l.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rust_pgn_reader_python_binding/__init__.py +5 -0
- rust_pgn_reader_python_binding/__init__.pyi +393 -0
- rust_pgn_reader_python_binding/py.typed +0 -0
- rust_pgn_reader_python_binding/rust_pgn_reader_python_binding.cpython-315-arm-linux-musleabihf.so +0 -0
- rust_pgn_reader_python_binding-4.2.0.dist-info/METADATA +99 -0
- rust_pgn_reader_python_binding-4.2.0.dist-info/RECORD +9 -0
- rust_pgn_reader_python_binding-4.2.0.dist-info/WHEEL +4 -0
- rust_pgn_reader_python_binding-4.2.0.dist-info/sboms/rust_pgn_reader_python_binding.cyclonedx.json +3221 -0
- rust_pgn_reader_python_binding.libs/libgcc_s-262c4f60.so.1 +0 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
from typing import List, Optional, Tuple, Dict, Iterator, Union, overload
|
|
2
|
+
import pyarrow
|
|
3
|
+
import numpy as np
|
|
4
|
+
import numpy.typing as npt
|
|
5
|
+
from numpy.typing import NDArray
|
|
6
|
+
|
|
7
|
+
class PyGameView:
|
|
8
|
+
"""Zero-copy view into a single game's data within a ParsedGames result.
|
|
9
|
+
|
|
10
|
+
Board indexing note: Boards use square indexing (a1=0, h8=63).
|
|
11
|
+
To convert to rank/file:
|
|
12
|
+
rank = square // 8
|
|
13
|
+
file = square % 8
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __len__(self) -> int:
|
|
17
|
+
"""Number of moves in this game."""
|
|
18
|
+
...
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def num_positions(self) -> int:
|
|
22
|
+
"""Number of positions recorded for this game."""
|
|
23
|
+
...
|
|
24
|
+
|
|
25
|
+
# === Board state views ===
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def boards(self) -> NDArray[np.uint8]:
|
|
29
|
+
"""Board positions, shape (num_positions, 8, 8)."""
|
|
30
|
+
...
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def initial_board(self) -> NDArray[np.uint8]:
|
|
34
|
+
"""Initial board position, shape (8, 8)."""
|
|
35
|
+
...
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def final_board(self) -> NDArray[np.uint8]:
|
|
39
|
+
"""Final board position, shape (8, 8)."""
|
|
40
|
+
...
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def castling(self) -> NDArray[np.bool_]:
|
|
44
|
+
"""Castling rights [K,Q,k,q], shape (num_positions, 4)."""
|
|
45
|
+
...
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def en_passant(self) -> NDArray[np.int8]:
|
|
49
|
+
"""En passant file (-1 if none), shape (num_positions,)."""
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def halfmove_clock(self) -> NDArray[np.uint8]:
|
|
54
|
+
"""Halfmove clock, shape (num_positions,)."""
|
|
55
|
+
...
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def turn(self) -> NDArray[np.bool_]:
|
|
59
|
+
"""Side to move (True=white), shape (num_positions,)."""
|
|
60
|
+
...
|
|
61
|
+
|
|
62
|
+
# === Move views ===
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def from_squares(self) -> NDArray[np.uint8]:
|
|
66
|
+
"""From squares, shape (num_moves,)."""
|
|
67
|
+
...
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def to_squares(self) -> NDArray[np.uint8]:
|
|
71
|
+
"""To squares, shape (num_moves,)."""
|
|
72
|
+
...
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def promotions(self) -> NDArray[np.int8]:
|
|
76
|
+
"""Promotions (-1=none, 2=N, 3=B, 4=R, 5=Q), shape (num_moves,)."""
|
|
77
|
+
...
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def clocks(self) -> NDArray[np.float32]:
|
|
81
|
+
"""Clock times in seconds (NaN if missing), shape (num_moves,)."""
|
|
82
|
+
...
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def evals(self) -> NDArray[np.float32]:
|
|
86
|
+
"""Engine evals (NaN if missing), shape (num_moves,)."""
|
|
87
|
+
...
|
|
88
|
+
|
|
89
|
+
# === Per-game metadata ===
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def headers(self) -> Dict[str, str]:
|
|
93
|
+
"""Raw PGN headers as dict."""
|
|
94
|
+
...
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def outcome(self) -> Optional[str]:
|
|
98
|
+
"""Game outcome from movetext: 'White', 'Black', 'Draw', 'Unknown', or None."""
|
|
99
|
+
...
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def parse_error(self) -> Optional[str]:
|
|
103
|
+
"""Parse error message if the game failed to parse, or None if valid."""
|
|
104
|
+
...
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def is_checkmate(self) -> bool:
|
|
108
|
+
"""Final position is checkmate."""
|
|
109
|
+
...
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def is_stalemate(self) -> bool:
|
|
113
|
+
"""Final position is stalemate."""
|
|
114
|
+
...
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def is_insufficient(self) -> Tuple[bool, bool]:
|
|
118
|
+
"""Insufficient material (white, black)."""
|
|
119
|
+
...
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def legal_move_count(self) -> int:
|
|
123
|
+
"""Legal move count in final position."""
|
|
124
|
+
...
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def is_valid(self) -> bool:
|
|
128
|
+
"""Whether game parsed successfully."""
|
|
129
|
+
...
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def is_game_over(self) -> bool:
|
|
133
|
+
"""Whether the game is over (checkmate, stalemate, or both sides insufficient)."""
|
|
134
|
+
...
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def comments(self) -> List[Optional[str]]:
|
|
138
|
+
"""Raw text comments per move (only populated when store_comments=True)."""
|
|
139
|
+
...
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def legal_moves(self) -> List[List[Tuple[int, int, int]]]:
|
|
143
|
+
"""Legal moves at each position (only populated when store_legal_moves=True).
|
|
144
|
+
Each entry is a list of (from_square, to_square, promotion) tuples."""
|
|
145
|
+
...
|
|
146
|
+
|
|
147
|
+
# === Convenience methods ===
|
|
148
|
+
|
|
149
|
+
def move_uci(self, move_idx: int) -> str:
|
|
150
|
+
"""Get UCI string for move at index."""
|
|
151
|
+
...
|
|
152
|
+
|
|
153
|
+
def moves_uci(self) -> List[str]:
|
|
154
|
+
"""Get all moves as UCI strings."""
|
|
155
|
+
...
|
|
156
|
+
|
|
157
|
+
def __repr__(self) -> str: ...
|
|
158
|
+
|
|
159
|
+
class ParsedGamesIter:
|
|
160
|
+
"""Iterator over games in a ParsedGames result."""
|
|
161
|
+
|
|
162
|
+
def __iter__(self) -> "ParsedGamesIter": ...
|
|
163
|
+
def __next__(self) -> PyGameView: ...
|
|
164
|
+
|
|
165
|
+
class PyChunkView:
|
|
166
|
+
"""View into a single chunk's raw numpy arrays.
|
|
167
|
+
|
|
168
|
+
Access via ``parsed_games.chunks[i]``. Each chunk corresponds to one
|
|
169
|
+
parsing thread's output. Use this for advanced access patterns like
|
|
170
|
+
manual concatenation or custom batching.
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def num_games(self) -> int: ...
|
|
175
|
+
@property
|
|
176
|
+
def num_moves(self) -> int: ...
|
|
177
|
+
@property
|
|
178
|
+
def num_positions(self) -> int: ...
|
|
179
|
+
@property
|
|
180
|
+
def boards(self) -> NDArray[np.uint8]:
|
|
181
|
+
"""Board positions, shape (N_positions, 8, 8), dtype uint8."""
|
|
182
|
+
...
|
|
183
|
+
@property
|
|
184
|
+
def castling(self) -> NDArray[np.bool_]:
|
|
185
|
+
"""Castling rights [K,Q,k,q], shape (N_positions, 4), dtype bool."""
|
|
186
|
+
...
|
|
187
|
+
@property
|
|
188
|
+
def en_passant(self) -> NDArray[np.int8]: ...
|
|
189
|
+
@property
|
|
190
|
+
def halfmove_clock(self) -> NDArray[np.uint8]: ...
|
|
191
|
+
@property
|
|
192
|
+
def turn(self) -> NDArray[np.bool_]: ...
|
|
193
|
+
@property
|
|
194
|
+
def from_squares(self) -> NDArray[np.uint8]: ...
|
|
195
|
+
@property
|
|
196
|
+
def to_squares(self) -> NDArray[np.uint8]: ...
|
|
197
|
+
@property
|
|
198
|
+
def promotions(self) -> NDArray[np.int8]: ...
|
|
199
|
+
@property
|
|
200
|
+
def clocks(self) -> NDArray[np.float32]: ...
|
|
201
|
+
@property
|
|
202
|
+
def evals(self) -> NDArray[np.float32]: ...
|
|
203
|
+
@property
|
|
204
|
+
def move_offsets(self) -> NDArray[np.uint32]: ...
|
|
205
|
+
@property
|
|
206
|
+
def position_offsets(self) -> NDArray[np.uint32]: ...
|
|
207
|
+
@property
|
|
208
|
+
def is_checkmate(self) -> NDArray[np.bool_]: ...
|
|
209
|
+
@property
|
|
210
|
+
def is_stalemate(self) -> NDArray[np.bool_]: ...
|
|
211
|
+
@property
|
|
212
|
+
def is_insufficient(self) -> NDArray[np.bool_]: ...
|
|
213
|
+
@property
|
|
214
|
+
def legal_move_count(self) -> NDArray[np.uint16]: ...
|
|
215
|
+
@property
|
|
216
|
+
def valid(self) -> NDArray[np.bool_]: ...
|
|
217
|
+
@property
|
|
218
|
+
def headers(self) -> List[Dict[str, str]]: ...
|
|
219
|
+
@property
|
|
220
|
+
def outcome(self) -> List[Optional[str]]: ...
|
|
221
|
+
@property
|
|
222
|
+
def parse_errors(self) -> List[Optional[str]]: ...
|
|
223
|
+
@property
|
|
224
|
+
def comments(self) -> List[Optional[str]]: ...
|
|
225
|
+
@property
|
|
226
|
+
def legal_move_from_squares(self) -> NDArray[np.uint8]: ...
|
|
227
|
+
@property
|
|
228
|
+
def legal_move_to_squares(self) -> NDArray[np.uint8]: ...
|
|
229
|
+
@property
|
|
230
|
+
def legal_move_promotions(self) -> NDArray[np.int8]: ...
|
|
231
|
+
@property
|
|
232
|
+
def legal_move_offsets(self) -> NDArray[np.uint32]: ...
|
|
233
|
+
def __repr__(self) -> str: ...
|
|
234
|
+
|
|
235
|
+
class ParsedGames:
|
|
236
|
+
"""Chunked container for parsed chess games, optimized for ML training.
|
|
237
|
+
|
|
238
|
+
Internally stores data in multiple chunks (one per parsing thread) to
|
|
239
|
+
avoid the cost of merging. Per-game access is O(log(num_chunks)) via
|
|
240
|
+
binary search on precomputed boundaries.
|
|
241
|
+
|
|
242
|
+
Board layout:
|
|
243
|
+
Boards use square indexing: a1=0, b1=1, ..., h8=63
|
|
244
|
+
Piece encoding: 0=empty, 1-6=white PNBRQK, 7-12=black pnbrqk
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
# === Computed properties ===
|
|
248
|
+
|
|
249
|
+
@property
|
|
250
|
+
def num_games(self) -> int:
|
|
251
|
+
"""Number of games in the result."""
|
|
252
|
+
...
|
|
253
|
+
|
|
254
|
+
@property
|
|
255
|
+
def num_moves(self) -> int:
|
|
256
|
+
"""Total number of moves across all games."""
|
|
257
|
+
...
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def num_positions(self) -> int:
|
|
261
|
+
"""Total number of board positions recorded."""
|
|
262
|
+
...
|
|
263
|
+
|
|
264
|
+
@property
|
|
265
|
+
def num_chunks(self) -> int:
|
|
266
|
+
"""Number of internal chunks."""
|
|
267
|
+
...
|
|
268
|
+
|
|
269
|
+
# === Escape hatch: raw chunk access ===
|
|
270
|
+
|
|
271
|
+
@property
|
|
272
|
+
def chunks(self) -> List[PyChunkView]:
|
|
273
|
+
"""Access raw per-chunk data.
|
|
274
|
+
|
|
275
|
+
Each chunk corresponds to one parsing thread's output. Use this
|
|
276
|
+
for advanced access patterns like manual concatenation.
|
|
277
|
+
"""
|
|
278
|
+
...
|
|
279
|
+
|
|
280
|
+
# === Sequence protocol ===
|
|
281
|
+
|
|
282
|
+
def __len__(self) -> int:
|
|
283
|
+
"""Number of games in the result."""
|
|
284
|
+
...
|
|
285
|
+
|
|
286
|
+
@overload
|
|
287
|
+
def __getitem__(self, idx: int) -> PyGameView: ...
|
|
288
|
+
@overload
|
|
289
|
+
def __getitem__(self, idx: slice) -> List[PyGameView]: ...
|
|
290
|
+
def __getitem__(
|
|
291
|
+
self, idx: Union[int, slice]
|
|
292
|
+
) -> Union[PyGameView, List[PyGameView]]:
|
|
293
|
+
"""Access game(s) by index or slice."""
|
|
294
|
+
...
|
|
295
|
+
|
|
296
|
+
def __iter__(self) -> ParsedGamesIter:
|
|
297
|
+
"""Iterate over all games."""
|
|
298
|
+
...
|
|
299
|
+
|
|
300
|
+
def __repr__(self) -> str: ...
|
|
301
|
+
|
|
302
|
+
# === Mapping utilities ===
|
|
303
|
+
|
|
304
|
+
def position_to_game(self, position_indices: npt.ArrayLike) -> NDArray[np.int64]:
|
|
305
|
+
"""Map global position indices to game indices.
|
|
306
|
+
|
|
307
|
+
Useful after shuffling/sampling positions to look up game metadata.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
position_indices: Array of indices into the global position space.
|
|
311
|
+
Accepts any integer dtype; int64 is optimal (avoids conversion).
|
|
312
|
+
|
|
313
|
+
Returns:
|
|
314
|
+
Array of game indices (same shape as input)
|
|
315
|
+
"""
|
|
316
|
+
...
|
|
317
|
+
|
|
318
|
+
def move_to_game(self, move_indices: npt.ArrayLike) -> NDArray[np.int64]:
|
|
319
|
+
"""Map global move indices to game indices.
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
move_indices: Array of indices into the global move space.
|
|
323
|
+
Accepts any integer dtype; int64 is optimal (avoids conversion).
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
Array of game indices (same shape as input)
|
|
327
|
+
"""
|
|
328
|
+
...
|
|
329
|
+
|
|
330
|
+
def parse_game(
|
|
331
|
+
pgn: str,
|
|
332
|
+
store_comments: bool = False,
|
|
333
|
+
store_legal_moves: bool = False,
|
|
334
|
+
) -> ParsedGames:
|
|
335
|
+
"""Parse a single PGN game string.
|
|
336
|
+
|
|
337
|
+
Convenience wrapper for parsing a single game. Returns a ParsedGames
|
|
338
|
+
container with one game.
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
pgn: PGN game string
|
|
342
|
+
store_comments: Whether to store raw text comments (default: False)
|
|
343
|
+
store_legal_moves: Whether to store legal moves at each position (default: False)
|
|
344
|
+
|
|
345
|
+
Returns:
|
|
346
|
+
ParsedGames object containing the parsed game
|
|
347
|
+
"""
|
|
348
|
+
...
|
|
349
|
+
|
|
350
|
+
def parse_games(
|
|
351
|
+
pgn_chunked_array: pyarrow.ChunkedArray,
|
|
352
|
+
num_threads: Optional[int] = None,
|
|
353
|
+
chunk_multiplier: Optional[int] = None,
|
|
354
|
+
store_comments: bool = False,
|
|
355
|
+
store_legal_moves: bool = False,
|
|
356
|
+
) -> ParsedGames:
|
|
357
|
+
"""Parse chess games from a PyArrow ChunkedArray into flat NumPy arrays.
|
|
358
|
+
|
|
359
|
+
This API is optimized for ML training pipelines, returning flat NumPy arrays
|
|
360
|
+
that can be efficiently batched and processed.
|
|
361
|
+
|
|
362
|
+
Args:
|
|
363
|
+
pgn_chunked_array: PyArrow ChunkedArray containing PGN strings
|
|
364
|
+
num_threads: Number of threads for parallel parsing (default: all CPUs)
|
|
365
|
+
chunk_multiplier: Multiplier for number of chunks (default: 1)
|
|
366
|
+
store_comments: Whether to store raw text comments (default: False)
|
|
367
|
+
store_legal_moves: Whether to store legal moves at each position (default: False)
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
ParsedGames object containing flat arrays and iteration support
|
|
371
|
+
"""
|
|
372
|
+
...
|
|
373
|
+
|
|
374
|
+
def parse_games_from_strings(
|
|
375
|
+
pgns: List[str],
|
|
376
|
+
num_threads: Optional[int] = None,
|
|
377
|
+
store_comments: bool = False,
|
|
378
|
+
store_legal_moves: bool = False,
|
|
379
|
+
) -> ParsedGames:
|
|
380
|
+
"""Parse multiple PGN game strings in parallel.
|
|
381
|
+
|
|
382
|
+
Convenience wrapper for when you have a list of strings rather than an Arrow array.
|
|
383
|
+
|
|
384
|
+
Args:
|
|
385
|
+
pgns: List of PGN game strings
|
|
386
|
+
num_threads: Number of threads for parallel parsing (default: all CPUs)
|
|
387
|
+
store_comments: Whether to store raw text comments (default: False)
|
|
388
|
+
store_legal_moves: Whether to store legal moves at each position (default: False)
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
ParsedGames object containing flat arrays and iteration support
|
|
392
|
+
"""
|
|
393
|
+
...
|
|
File without changes
|
rust_pgn_reader_python_binding/rust_pgn_reader_python_binding.cpython-315-arm-linux-musleabihf.so
ADDED
|
Binary file
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rust_pgn_reader_python_binding
|
|
3
|
+
Version: 4.2.0
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
7
|
+
Requires-Dist: numpy
|
|
8
|
+
Requires-Dist: pyarrow
|
|
9
|
+
Summary: A fast chess PGN & parquet parser
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
12
|
+
Project-URL: Homepage, https://github.com/vladkvit/rust_pgn_reader_python_binding
|
|
13
|
+
Project-URL: Repository, https://github.com/vladkvit/rust_pgn_reader_python_binding.git
|
|
14
|
+
|
|
15
|
+
# rust_pgn_reader_python_binding
|
|
16
|
+
## Fast PGN parsing bindings for Python
|
|
17
|
+
This project adds Python bindings to [rust-pgn-reader](https://github.com/niklasf/rust-pgn-reader). In addition, it also parses and extracts [%clk ..] and [%eval ..] tags from comments.
|
|
18
|
+
|
|
19
|
+
## Installing
|
|
20
|
+
`pip install rust_pgn_reader_python_binding`
|
|
21
|
+
|
|
22
|
+
## API
|
|
23
|
+
|
|
24
|
+
Three entry points are available:
|
|
25
|
+
|
|
26
|
+
- `parse_game(pgn)` - Parse a single PGN string
|
|
27
|
+
- `parse_games(chunked_array)` - Parse games from a PyArrow ChunkedArray (multithreaded)
|
|
28
|
+
- `parse_games_from_strings(pgns)` - Parse a list of PGN strings (multithreaded)
|
|
29
|
+
|
|
30
|
+
All return a `ParsedGames` container with flat NumPy arrays, supporting:
|
|
31
|
+
- Indexing (`result[i]`), slicing (`result[1:3]`), and iteration (`for game in result`)
|
|
32
|
+
- Per-game views (`PyGameView`) with zero-copy array slices
|
|
33
|
+
- Position-to-game and move-to-game mapping for ML workflows
|
|
34
|
+
- Optional comment storage (`store_comments=True`)
|
|
35
|
+
- Optional legal move storage (`store_legal_moves=True`)
|
|
36
|
+
|
|
37
|
+
## Benchmarks
|
|
38
|
+
Below are some benchmarks on Lichess's 2013-07 chess games (293,459 games) on a 7800X3D.
|
|
39
|
+
|
|
40
|
+
| Parser | File format | Time |
|
|
41
|
+
|----------------------------------------------------------------------------|-------------|--------|
|
|
42
|
+
| **rust_pgn_reader_python_binding, parse_games (multithreaded)** | parquet | 0.14s |
|
|
43
|
+
| rust_pgn_reader_python_binding, parse_games (singlethreaded) | parquet | 1.3s |
|
|
44
|
+
| rust_pgn_reader_python_binding, parse_games_from_strings (multithreaded) | PGN | 0.41s |
|
|
45
|
+
| [chess-library](https://github.com/Disservin/chess-library) | PGN | 2s |
|
|
46
|
+
| [rust-pgn-reader](https://github.com/niklasf/rust-pgn-reader/tree/master) | PGN | 1s |
|
|
47
|
+
| [python-chess](https://github.com/niklasf/python-chess) | PGN | 3+ min |
|
|
48
|
+
|
|
49
|
+
To replicate, download `2013-07-train-00000-of-00001.parquet` and then run:
|
|
50
|
+
|
|
51
|
+
`python src/bench_parse_games.py` (recommended — multithreaded parse_games via Arrow)
|
|
52
|
+
|
|
53
|
+
`python src/bench_parse_games_singlethreaded.py` (singlethreaded parse_games via Arrow)
|
|
54
|
+
|
|
55
|
+
`python src/bench_parse_pgn.py` (multithreaded .pgn parsing)
|
|
56
|
+
|
|
57
|
+
`python src/bench_data_access.py 2013-07-train-00000-of-00001.parquet` (parsing + data access + memory)
|
|
58
|
+
|
|
59
|
+
## Building
|
|
60
|
+
`maturin develop`
|
|
61
|
+
|
|
62
|
+
`maturin develop --release`
|
|
63
|
+
|
|
64
|
+
For a more thorough tutorial, follow https://lukesalamone.github.io/posts/how-to-create-rust-python-bindings/
|
|
65
|
+
|
|
66
|
+
## Profiling
|
|
67
|
+
`samply record --rate 10000 python src\bench_parse_games.py`
|
|
68
|
+
`py-spy record -s -F -f speedscope --output profile.speedscope -- python ./src/bench_parse_games.py`
|
|
69
|
+
|
|
70
|
+
Linux/WSL-only:
|
|
71
|
+
`py-spy record -s -F -n -f speedscope --output profile.speedscope -- python ./src/bench_parse_games.py`
|
|
72
|
+
|
|
73
|
+
## Testing
|
|
74
|
+
`cargo test`
|
|
75
|
+
|
|
76
|
+
`python -m unittest src/test.py`
|
|
77
|
+
|
|
78
|
+
## Type stubs
|
|
79
|
+
We have a manual `rust_pgn_reader_python_binding.pyi` python type stub.
|
|
80
|
+
To keep it in sync with the Rust bindings, run:
|
|
81
|
+
|
|
82
|
+
`python tools/check_stubs.py`
|
|
83
|
+
|
|
84
|
+
This regenerates the stub via `maturin generate-stubs` (PyO3
|
|
85
|
+
`experimental-inspect`, behind the `stubgen` Cargo feature) and diffs the
|
|
86
|
+
symbol surface (module functions, classes, methods/properties, but not
|
|
87
|
+
annotations) against the hand-written file. CI runs the same check in the
|
|
88
|
+
`stubs` job.
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
## Further performance squeezing:
|
|
92
|
+
- Interleaved parquet read + parse
|
|
93
|
+
- Opening cache. For positions that occur plenty of times, a `(zobrist(pos), san_token)` → `Move`
|
|
94
|
+
cache could skip `san_resolver` + legality on cache hits.
|
|
95
|
+
- crude move-count estimator from movetext byte length for buffer preallocation (bytes-per-ply ≈ 6-6.5, over-estimate
|
|
96
|
+
is relatively cheap, under-estimate forces realloc copies). We currently assume 70 moves per game. Since we have long chunks (not per-game),
|
|
97
|
+
long game moves eat up the slack from short games. Wont' help on Lichess games (we're already doing quite well). But if going with a
|
|
98
|
+
different / unknown corpus, 70 will be off (different game lengths, comments). But comments will also throw off our byte-based move count estimator.
|
|
99
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
rust_pgn_reader_python_binding/__init__.py,sha256=lhSdJaKZEMrZVdzPOP73MTNFcg6hOIbqlkQ070gWYAQ,203
|
|
2
|
+
rust_pgn_reader_python_binding/__init__.pyi,sha256=reLZezmDzhgf918Nz3BQAgiv8PI1brq0UI6uzVdlsJk,11542
|
|
3
|
+
rust_pgn_reader_python_binding/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
rust_pgn_reader_python_binding/rust_pgn_reader_python_binding.cpython-315-arm-linux-musleabihf.so,sha256=OegcECjiDIBZkc_hodHt2CypLWm3ycoFfRH_5argmo0,16626733
|
|
5
|
+
rust_pgn_reader_python_binding-4.2.0.dist-info/METADATA,sha256=Ba9f6gB_q4GCpn9AUNAmvNDEC1F3hRFHFa9zWyfBEao,4689
|
|
6
|
+
rust_pgn_reader_python_binding-4.2.0.dist-info/WHEEL,sha256=7vKPdusLGuyoFe-7QFlLo7up40KfuBKCpQAnzbdKWtc,108
|
|
7
|
+
rust_pgn_reader_python_binding-4.2.0.dist-info/sboms/rust_pgn_reader_python_binding.cyclonedx.json,sha256=2szTLTDLkhk3OUAP_65bHm94Wl9WEXwSRfczDWHKhqQ,102729
|
|
8
|
+
rust_pgn_reader_python_binding.libs/libgcc_s-262c4f60.so.1,sha256=fA1fr7_M_sZmVSS8yaamft5FzaZdrq_bTxQOPu00OFE,2811941
|
|
9
|
+
rust_pgn_reader_python_binding-4.2.0.dist-info/RECORD,,
|