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 +217 -0
- gf2-0.1.0/CHANGELOG.md +128 -0
- gf2-0.1.0/LICENSE +21 -0
- gf2-0.1.0/PKG-INFO +245 -0
- gf2-0.1.0/README.md +183 -0
- gf2-0.1.0/gf2/__init__.py +87 -0
- gf2-0.1.0/gf2/core.py +798 -0
- gf2-0.1.0/gf2/generators.py +537 -0
- gf2-0.1.0/gf2/py.typed +0 -0
- gf2-0.1.0/gf2/solvers.py +576 -0
- gf2-0.1.0/gf2/sparse.py +681 -0
- gf2-0.1.0/pyproject.toml +173 -0
- gf2-0.1.0/tests/conftest.py +317 -0
- gf2-0.1.0/tests/test_algebraic_properties.py +500 -0
- gf2-0.1.0/tests/test_core.py +31 -0
- gf2-0.1.0/tests/test_core_ops.py +593 -0
- gf2-0.1.0/tests/test_generators.py +1101 -0
- gf2-0.1.0/tests/test_matrix_properties.py +623 -0
- gf2-0.1.0/tests/test_properties.py +135 -0
- gf2-0.1.0/tests/test_release_invariants.py +610 -0
- gf2-0.1.0/tests/test_review_regressions.py +441 -0
- gf2-0.1.0/tests/test_solver_properties.py +528 -0
- gf2-0.1.0/tests/test_solvers.py +641 -0
- gf2-0.1.0/tests/test_sparse.py +1554 -0
- gf2-0.1.0/tests/test_utils.py +626 -0
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
|
+
[](https://github.com/kkKaan/gf2/actions)
|
|
68
|
+
[](https://www.python.org/downloads/)
|
|
69
|
+
[](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.
|