gf2 0.1.0__tar.gz

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.
gf2-0.1.0/.gitignore ADDED
@@ -0,0 +1,217 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ #uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+ #poetry.toml
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
115
+ #pdm.lock
116
+ #pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # pixi
121
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122
+ #pixi.lock
123
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124
+ # in the .venv directory. It is recommended not to include this directory in version control.
125
+ .pixi
126
+
127
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
128
+ __pypackages__/
129
+
130
+ # Celery stuff
131
+ celerybeat-schedule
132
+ celerybeat.pid
133
+
134
+ # SageMath parsed files
135
+ *.sage.py
136
+
137
+ # Environments
138
+ .env
139
+ .envrc
140
+ .venv
141
+ env/
142
+ venv/
143
+ ENV/
144
+ env.bak/
145
+ venv.bak/
146
+
147
+ # Spyder project settings
148
+ .spyderproject
149
+ .spyproject
150
+
151
+ # Rope project settings
152
+ .ropeproject
153
+
154
+ # mkdocs documentation
155
+ /site
156
+
157
+ # mypy
158
+ .mypy_cache/
159
+ .dmypy.json
160
+ dmypy.json
161
+
162
+ # Pyre type checker
163
+ .pyre/
164
+
165
+ # pytype static type analyzer
166
+ .pytype/
167
+
168
+ # Cython debug symbols
169
+ cython_debug/
170
+
171
+ # PyCharm
172
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
173
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
174
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
175
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
176
+ #.idea/
177
+
178
+ # Abstra
179
+ # Abstra is an AI-powered process automation framework.
180
+ # Ignore directories containing user credentials, local state, and settings.
181
+ # Learn more at https://abstra.io/docs
182
+ .abstra/
183
+
184
+ # Visual Studio Code
185
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
186
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
187
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
188
+ # you could uncomment the following to ignore the entire vscode folder
189
+ # .vscode/
190
+
191
+ # Ruff stuff:
192
+ .ruff_cache/
193
+
194
+ # PyPI configuration file
195
+ .pypirc
196
+
197
+ # Cursor
198
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
199
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
200
+ # refer to https://docs.cursor.com/context/ignore-files
201
+ .cursorignore
202
+ .cursorindexingignore
203
+
204
+ # Marimo
205
+ marimo/_static/
206
+ marimo/_lsp/
207
+ __marimo__/
208
+
209
+ # Ruff cache
210
+ .ruff_cache/
211
+
212
+ # macOS
213
+ .DS_Store
214
+
215
+ .kiro/
216
+ # graphify knowledge graph (regenerate with: graphify update .)
217
+ graphify-out/
gf2-0.1.0/CHANGELOG.md ADDED
@@ -0,0 +1,128 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project
5
+ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - 2026-09-05
10
+
11
+ First public release.
12
+
13
+ ### Renamed
14
+
15
+ - The project was developed as `binpy`. That name belongs to an unrelated
16
+ digital-logic package on PyPI, so the import root is `gf2`. There was no
17
+ public `binpy` release, so no upgrade path is needed: use `import gf2`.
18
+
19
+ ### Added
20
+
21
+ - `SparseGF2Matrix` / `DenseGF2Matrix` with automatic CSR / bit-packed storage
22
+ selection, and `create_sparse_matrix`.
23
+ - Core arithmetic: `add`, `multiply`, `transpose`, `rank`, `det`, `trace`,
24
+ `is_invertible`, `reduced_row_echelon_form`, `lu_decomposition`,
25
+ `matrix_power`, `characteristic_polynomial`, `minimal_polynomial`.
26
+ - Solvers: `solve`, `solve_multiple_rhs`, `nullspace`, `nullspace_bitwise`,
27
+ `nullspace_fast`, `inverse`, `kernel`, `image`, `rank_nullity_theorem`,
28
+ `iterative_refinement`.
29
+ - Generators: `identity`, `zeros`, `ones`, `random_sparse`, `random_regular`,
30
+ `circulant`, `circulant_random`, `toeplitz`, `ldpc_matrix`,
31
+ `hamming_matrix`, `repetition_matrix`.
32
+ - Quantum code constructions: `hypergraph_product` (Tillich-Zemor),
33
+ `surface_code_matrix` (planar, built as a hypergraph product of repetition
34
+ codes), `css_code_matrix`, `bicycle_codes`.
35
+ - `py.typed`: the package is annotated throughout and ships its types.
36
+ - Structural equality on both matrix types: `A == B` compares shape and
37
+ contents, independently of storage format, and a sparse matrix compares equal
38
+ to a dense one holding the same bits. Previously `==` fell through to
39
+ identity and returned False for equal matrices. Defining `__eq__` makes the
40
+ types unhashable, which is correct: they are mutable through `set_bit`.
41
+ - Operations returning several values return NamedTuples -- `LUDecomposition`,
42
+ `RowEchelonForm`, `NullspaceVector`, `RankNullity` -- so a caller can write
43
+ `result.pivot_columns` instead of `result[1]`. They unpack positionally
44
+ exactly like the plain tuples they replace.
45
+ - `lu_decomposition` returns `(L, U, perm)` with `A[perm] == L @ U`, plus
46
+ `LUDecomposition.permutation_matrix()` for the equivalent `P @ A == L @ U`
47
+ form.
48
+ - A benchmark suite (`benchmarks/`) with a documented measurement methodology,
49
+ and a report generated from recorded measurements rather than written by hand.
50
+
51
+ ### Performance
52
+
53
+ Relative to the pre-release development tree, same inputs and warm caches:
54
+
55
+ - `multiply` 22.9x faster at n=512 (Method of Four Russians over packed uint64;
56
+ row-XOR accumulation for sparse operands).
57
+ - `nullspace` basis extraction 49.3x faster on a rank-deficient 256x256.
58
+ - `transpose` 2.6x, `to_dense` 7.8x, `rank` 1.8x at n=512.
59
+ - `ldpc_matrix(method="progressive")` 49.6x faster; `ones` 508x.
60
+ - `minimal_polynomial` moved from O(2^n) enumeration to O(n^4 / 64) word
61
+ operations: n=16 went from 38 s to 0.18 ms.
62
+ - CSR row access no longer rebuilds each row per call; a full row sweep of a
63
+ 300x300 matrix went from 3.33 ms to 0.014 ms.
64
+
65
+ See `benchmarks/OPTIMIZATION_LOG.md`.
66
+
67
+ ### Fixed
68
+
69
+ Defects found during pre-release review, all of which failed silently:
70
+
71
+ - `set_bit` discarded the write on an empty-format matrix and left the
72
+ packed-row cache stale on the CSR path, so later reads saw the pre-write
73
+ matrix.
74
+ - `hypergraph_product` returned an all-zero `H_z`, so the CSS commutation
75
+ condition held only vacuously.
76
+ - `surface_code_matrix` declared more stabiliser rows than it populated and did
77
+ not satisfy `H_x @ H_z.T == 0`; it now produces a genuine planar surface code
78
+ with k = 1.
79
+ - `characteristic_polynomial` returned a stub that was wrong for every n >= 3.
80
+ - `nullspace_fast` accepted `include_packing_time` and ignored it.
81
+ - `iterative_refinement` mutated a NumPy right-hand side in place.
82
+ - Coordinate input now de-duplicates, bounds-checks, and handles zero
83
+ dimensions; packed rows are masked to the column count.
84
+ - `random_regular` could emit under-weight rows; seeded generators no longer
85
+ reseed the global `random` module.
86
+ - `lu_decomposition` swapped rows of U without recording the permutation or
87
+ applying it to L, so its output satisfied neither `A == L @ U` nor
88
+ `P @ A == L @ U`. It reconstructed A in roughly half of random cases and gave
89
+ the caller no way to recover the difference. It now returns the permutation
90
+ as a third element and reconstructs exactly, verified over 500 random
91
+ matrices. The existing test was named `..._reconstruction` but only asserted
92
+ the triangular shapes, which is how this survived.
93
+ - The packed representation assumed the host was little-endian: it paired
94
+ `int.to_bytes(..., "little")` with native-order NumPy arrays. On a big-endian
95
+ host (NumPy ships s390x wheels) a single set bit in word 0 read back as
96
+ 2**56, and `unpackbits` returned the wrong byte's bits - wrong answers rather
97
+ than an error. The storage dtype is now pinned to `"<u8"`, which is the
98
+ native dtype on little-endian hosts and therefore costs nothing there.
99
+
100
+ ### Removed
101
+
102
+ Cut before the first release rather than published under a name that promises
103
+ something else. None of these had users; all three were exported and would have
104
+ been a breaking change to withdraw later.
105
+
106
+ - `bch_matrix` was not a BCH parity check matrix. It applied a fixed arithmetic
107
+ mask (`(i+1)*(j+1) % 3 == 1`) with no coding-theoretic meaning and ignored
108
+ its error-correction parameter `t` entirely.
109
+ - `vandermonde` reduced integer powers mod 2. Since the parity of a power
110
+ depends only on the parity of the base, every row came out all-ones or
111
+ `[1, 0, 0, ...]`, giving a matrix of rank at most 2 — not a Vandermonde
112
+ matrix over GF(2^m).
113
+ - `color_code_matrix` did not satisfy `H_x @ H_z.T == 0`, so the pair it
114
+ returned was not a quantum code. Use `surface_code_matrix` or
115
+ `hypergraph_product`, which are exact by construction.
116
+
117
+ ### Known limitations
118
+
119
+ - Dense GF(2) multiplication is competitive with `galois` only from n = 1024;
120
+ below that `galois` is faster. Use it or `m4ri` if dense multiplication
121
+ dominates your workload.
122
+ - No extension-field GF(2^m) arithmetic. `vandermonde` and `bch_matrix` were
123
+ removed before release rather than shipped under names they did not earn
124
+ (see Removed); real versions need GF(2^m) and are deferred.
125
+ - Pure Python plus NumPy: no compiled kernel.
126
+
127
+ [Unreleased]: https://github.com/kkKaan/gf2/compare/v0.1.0...HEAD
128
+ [0.1.0]: https://github.com/kkKaan/gf2/releases/tag/v0.1.0
gf2-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Kaan Karaçanta
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
gf2-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,245 @@
1
+ Metadata-Version: 2.5
2
+ Name: gf2
3
+ Version: 0.1.0
4
+ Summary: High-performance binary (GF(2)) matrix operations for Python
5
+ Project-URL: Homepage, https://github.com/kkKaan/gf2
6
+ Project-URL: Source, https://github.com/kkKaan/gf2
7
+ Project-URL: Issues, https://github.com/kkKaan/gf2/issues
8
+ Author: Kaan Karaçanta
9
+ License: MIT License
10
+
11
+ Copyright (c) 2025 Kaan Karaçanta
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ License-File: LICENSE
31
+ Keywords: GF(2),LDPC,binary,bitwise,coding theory,linear algebra,matrix,sparse
32
+ Classifier: Development Status :: 3 - Alpha
33
+ Classifier: Intended Audience :: Developers
34
+ Classifier: Intended Audience :: Science/Research
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Programming Language :: Python
37
+ Classifier: Programming Language :: Python :: 3
38
+ Classifier: Programming Language :: Python :: 3 :: Only
39
+ Classifier: Programming Language :: Python :: 3.10
40
+ Classifier: Programming Language :: Python :: 3.11
41
+ Classifier: Programming Language :: Python :: 3.12
42
+ Classifier: Programming Language :: Python :: 3.13
43
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
44
+ Classifier: Topic :: Security :: Cryptography
45
+ Classifier: Typing :: Typed
46
+ Requires-Python: >=3.10
47
+ Requires-Dist: numpy>=1.23
48
+ Provides-Extra: bench
49
+ Requires-Dist: galois>=0.3; (python_version >= '3.10') and extra == 'bench'
50
+ Requires-Dist: matplotlib>=3.7; extra == 'bench'
51
+ Requires-Dist: scipy>=1.10; extra == 'bench'
52
+ Provides-Extra: dev
53
+ Requires-Dist: mypy==2.3.1; extra == 'dev'
54
+ Requires-Dist: pre-commit>=3.7; extra == 'dev'
55
+ Requires-Dist: ruff==0.16.6; extra == 'dev'
56
+ Provides-Extra: test
57
+ Requires-Dist: hypothesis>=6.90; extra == 'test'
58
+ Requires-Dist: psutil>=5.9; extra == 'test'
59
+ Requires-Dist: pytest-cov>=5.0; extra == 'test'
60
+ Requires-Dist: pytest>=8.0; extra == 'test'
61
+ Description-Content-Type: text/markdown
62
+
63
+ # gf2
64
+
65
+ High-performance binary (GF(2)) matrix operations library for Python.
66
+
67
+ [![CI](https://github.com/kkKaan/gf2/workflows/CI/badge.svg)](https://github.com/kkKaan/gf2/actions)
68
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
69
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
70
+
71
+ ## Installation
72
+
73
+ ```bash
74
+ # Install from source (development)
75
+ git clone https://github.com/kkKaan/gf2.git
76
+ cd gf2
77
+ pip install -e ".[dev,test]"
78
+ ```
79
+
80
+ ## Quick Start
81
+
82
+ ```python
83
+ import gf2
84
+
85
+ # Create matrices
86
+ A = gf2.identity(5) # 5x5 identity matrix
87
+ B = gf2.random_sparse(5, 5, density=0.3) # Random sparse matrix
88
+ C = gf2.zeros(3, 4) # 3x4 zero matrix
89
+
90
+ # Basic operations (all in GF(2))
91
+ sum_matrix = gf2.add(A, B) # XOR addition
92
+ product = gf2.multiply(A, B) # Binary matrix multiplication
93
+ A_transpose = gf2.transpose(A) # Matrix transpose
94
+
95
+ # Linear algebra
96
+ r = gf2.rank(A) # Matrix rank
97
+ det_A = gf2.det(A) # Determinant (0 or 1)
98
+ is_inv = gf2.is_invertible(A) # Invertibility check
99
+
100
+ # Solve linear systems Ax = b over GF(2)
101
+ b = [1, 0, 1, 0, 1]
102
+ x = gf2.solve(A, b) # Exact solution
103
+ null_space = gf2.nullspace(A) # Null space basis
104
+
105
+ # Matrix generators for coding theory
106
+ H = gf2.hamming_matrix(3) # Hamming code parity check
107
+ ldpc = gf2.ldpc_matrix(100, 200, row_weight=4) # LDPC code (m*row_weight must divide n)
108
+ circ = gf2.circulant([1, 0, 1, 1]) # Circulant matrix
109
+ ```
110
+
111
+ ## Advanced Usage
112
+
113
+ ### Custom Sparse Matrices
114
+
115
+ ```python
116
+ # Create from coordinates
117
+ coords = [(0, 1), (1, 2), (2, 0)] # (row, col) positions
118
+ matrix = gf2.create_sparse_matrix(3, 3, coordinates=coords)
119
+
120
+ # Different storage formats are automatically chosen
121
+ dense_like = gf2.random_sparse(10, 10, density=0.8) # Uses bit-packed storage
122
+ very_sparse = gf2.random_sparse(1000, 1000, density=0.01) # Uses CSR
123
+
124
+ # Access internal representation
125
+ print(matrix.memory_usage()) # Shows compression statistics
126
+ ```
127
+
128
+ ### Coding Theory Applications
129
+
130
+ ```python
131
+ # Generate LDPC codes
132
+ H = gf2.ldpc_matrix(m=500, n=1000, row_weight=6, method="progressive")
133
+
134
+ # Classical codes
135
+ hamming_H = gf2.hamming_matrix(r=4) # [15,11,3] Hamming code
136
+ rep_H = gf2.repetition_matrix(5) # length-5 repetition code
137
+
138
+ # Quantum codes (exact CSS commutation: H_x @ H_z.T == 0)
139
+ H_x, H_z = gf2.surface_code_matrix(distance=3) # planar surface code, k=1
140
+ Q_x, Q_z = gf2.hypergraph_product(hamming_H, rep_H) # Tillich-Zemor product
141
+
142
+ # Structured matrices
143
+ toeplitz_A = gf2.toeplitz([1, 0, 1], [1, 1, 0, 1])
144
+ circ = gf2.circulant([1, 0, 1, 1])
145
+ ```
146
+
147
+ ## Performance
148
+
149
+ gf2 stores rows bit-packed and does GF(2) arithmetic with whole-row bitwise
150
+ operations, so a row XOR costs one machine word per 64 columns instead of one
151
+ Python step per column.
152
+
153
+ - **Elimination** (rank, nullspace, solve, inverse) runs on Python big
154
+ integers below n = 384 and switches to vectorised NumPy uint64 rows above
155
+ it, because the crossover between the two was measured, not assumed.
156
+ - **Multiplication** uses the Method of Four Russians: one lookup table of
157
+ 2^8 pre-combined rows of B serves every row of A, so the XOR count drops
158
+ from O(mn/2) to O(mn/8 + n/8 * 256).
159
+ - **Storage** picks CSR or bit-packed automatically from the density.
160
+
161
+ ### Measured results
162
+
163
+ Numbers, methodology, and the exact environment live in
164
+ [`benchmarks/BENCHMARK_RESULTS.md`](https://github.com/kkKaan/gf2/blob/main/benchmarks/BENCHMARK_RESULTS.md), which is
165
+ **generated from `benchmarks/results.json`** rather than written by hand.
166
+
167
+ To reproduce:
168
+
169
+ ```bash
170
+ python benchmarks/bench_gf2.py # measure -> benchmarks/results.json
171
+ python benchmarks/make_report.py # results.json -> BENCHMARK_RESULTS.md
172
+ ```
173
+
174
+ Summary against the fastest honest rival at each size, square matrices at 50%
175
+ density, Python 3.11 / NumPy 2.2.6 / galois 0.4.6 on an arm64 Mac:
176
+
177
+ | operation | n=128 | n=512 | n=1024 |
178
+ |---|---|---|---|
179
+ | **rank** | **2.3x faster** than packed-NumPy | par with packed-NumPy | 1.1x slower than packed-NumPy |
180
+ | **nullspace vector** | **1.9x faster** than packed-NumPy | **1.5x faster** | **1.4x faster** |
181
+ | **multiply** | 5.9x slower than galois | 1.3x slower than galois | par with galois; **85x faster** than NumPy |
182
+
183
+ Against `galois`, gf2's rank is 12-27x faster and its nullspace 6-13x faster
184
+ across this range. Against a *naive* element-wise NumPy loop gf2 looks 9-13x
185
+ faster, but that comparison is not meaningful and the benchmark labels it as a
186
+ strawman: the baseline that matters is bit-packed uint64 NumPy, which is the
187
+ `numpy-packed` row in the report.
188
+
189
+ ### Honest limitations
190
+
191
+ - **Dense matrix multiply is not gf2's strength.** `galois` is faster below
192
+ n = 1024 and gf2 only draws level there. If dense GF(2) multiplication
193
+ dominates your workload, use `galois` or `m4ri`.
194
+ - **Peak memory during multiply is higher than NumPy's**, because the Four
195
+ Russians table and the unpacked selector are transient allocations. Bit
196
+ packing wins on *stored* size, not on scratch space.
197
+ - Everything here is pure Python plus NumPy. A C or Cython kernel would move
198
+ the elimination crossover a long way down.
199
+
200
+ ## Development
201
+
202
+ ```bash
203
+ # Clone and setup
204
+ git clone https://github.com/kkKaan/gf2.git
205
+ cd gf2
206
+ python -m venv .venv
207
+ source .venv/bin/activate # On Windows: .venv\Scripts\activate
208
+ pip install -e ".[dev,test]"
209
+
210
+ # Run tests
211
+ pytest tests/
212
+
213
+ # Code quality
214
+ ruff check . # Linting
215
+ ruff format . # Formatting
216
+ mypy gf2/ # Type checking
217
+
218
+ # Install pre-commit hooks
219
+ pre-commit install
220
+ ```
221
+
222
+ ## Simon's Algorithm Postprocessing
223
+
224
+ gf2 provides fast GF(2) nullspace routines used in Simon-style workflows:
225
+
226
+ - High-level basis: `gf2.nullspace(A)` returns a basis as a list of 0/1 lists
227
+ - Fast bitwise single solution: `gf2.nullspace_bitwise(A)` -> `(solution_bits: str, seconds: float)`
228
+ - Zero-overhead raw input: `gf2.nullspace_fast(matrix)` -> `(solution_bits: str, seconds: float)`
229
+
230
+ Example using `nullspace_fast` directly on list-of-lists:
231
+
232
+ ```python
233
+ from gf2 import nullspace_fast
234
+
235
+ matrix = [
236
+ [1, 0, 1, 0, 1],
237
+ [0, 1, 1, 0, 0],
238
+ [1, 1, 0, 1, 0], # use n-1 rows for underdetermined system
239
+ ]
240
+
241
+ solution_bits, elapsed = nullspace_fast(matrix)
242
+ print(solution_bits, elapsed)
243
+ ```
244
+
245
+ This returns a nontrivial nullspace vector as a binary string and the elapsed time, matching usage patterns in Simon postprocessing scripts.