patchcraft 0.5.2__tar.gz → 0.5.3__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.
Files changed (86) hide show
  1. patchcraft-0.5.3/.pre-commit-config.yaml +45 -0
  2. patchcraft-0.5.3/AGENTS.md +137 -0
  3. {patchcraft-0.5.2 → patchcraft-0.5.3}/CHANGELOG.md +107 -1
  4. patchcraft-0.5.3/CITATION.cff +51 -0
  5. patchcraft-0.5.3/CODE_OF_CONDUCT.md +83 -0
  6. {patchcraft-0.5.2 → patchcraft-0.5.3}/CONTRIBUTING.md +38 -3
  7. {patchcraft-0.5.2 → patchcraft-0.5.3}/MANIFEST.in +7 -2
  8. patchcraft-0.5.3/MAP.md +107 -0
  9. {patchcraft-0.5.2 → patchcraft-0.5.3}/PKG-INFO +3 -3
  10. {patchcraft-0.5.2 → patchcraft-0.5.3}/README.md +3 -2
  11. {patchcraft-0.5.2 → patchcraft-0.5.3}/README.pt-BR.md +3 -2
  12. {patchcraft-0.5.2 → patchcraft-0.5.3}/README.pypi.md +2 -2
  13. patchcraft-0.5.3/SECURITY.md +66 -0
  14. patchcraft-0.5.3/STATUS.md +85 -0
  15. {patchcraft-0.5.2 → patchcraft-0.5.3}/docs/AUXILIARY.md +4 -2
  16. {patchcraft-0.5.2 → patchcraft-0.5.3}/docs/FOCO-1.0.md +14 -2
  17. {patchcraft-0.5.2 → patchcraft-0.5.3}/docs/GUIDE.md +4 -4
  18. {patchcraft-0.5.2 → patchcraft-0.5.3}/docs/SCOPE.md +1 -1
  19. patchcraft-0.5.3/docs/STUDIES/2026-09-03-strata-conformity.md +110 -0
  20. {patchcraft-0.5.2 → patchcraft-0.5.3}/docs/THEORY.md +2 -1
  21. {patchcraft-0.5.2 → patchcraft-0.5.3}/docs/USAGE.md +37 -26
  22. {patchcraft-0.5.2 → patchcraft-0.5.3}/pyproject.toml +4 -0
  23. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft/_version.py +3 -3
  24. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft/reconstruct.py +9 -3
  25. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft/stitch.py +4 -3
  26. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft.egg-info/PKG-INFO +3 -3
  27. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft.egg-info/SOURCES.txt +10 -0
  28. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft.egg-info/scm_file_list.json +17 -0
  29. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft.egg-info/scm_version.json +2 -2
  30. patchcraft-0.5.3/tests/test_citation.py +51 -0
  31. patchcraft-0.5.3/tests/test_docs_usage.py +34 -0
  32. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/test_reconstruct.py +36 -0
  33. {patchcraft-0.5.2 → patchcraft-0.5.3}/tools/check_dist.py +4 -2
  34. {patchcraft-0.5.2 → patchcraft-0.5.3}/.gitignore +0 -0
  35. {patchcraft-0.5.2 → patchcraft-0.5.3}/.python-version +0 -0
  36. {patchcraft-0.5.2 → patchcraft-0.5.3}/LICENSE +0 -0
  37. {patchcraft-0.5.2 → patchcraft-0.5.3}/accel/Cargo.lock +0 -0
  38. {patchcraft-0.5.2 → patchcraft-0.5.3}/accel/Cargo.toml +0 -0
  39. {patchcraft-0.5.2 → patchcraft-0.5.3}/accel/README.md +0 -0
  40. {patchcraft-0.5.2 → patchcraft-0.5.3}/accel/src/kernel.rs +0 -0
  41. {patchcraft-0.5.2 → patchcraft-0.5.3}/accel/src/lib.rs +0 -0
  42. {patchcraft-0.5.2 → patchcraft-0.5.3}/docs/ADR/0001-patch-extraction-api.md +0 -0
  43. {patchcraft-0.5.2 → patchcraft-0.5.3}/docs/ADR/0002-patchify-transform.md +0 -0
  44. {patchcraft-0.5.2 → patchcraft-0.5.3}/docs/ADR/0003-reversibility-classes.md +0 -0
  45. {patchcraft-0.5.2 → patchcraft-0.5.3}/docs/ADR/0004-precision-and-effort-parameters.md +0 -0
  46. {patchcraft-0.5.2 → patchcraft-0.5.3}/docs/PERFORMANCE.md +0 -0
  47. {patchcraft-0.5.2 → patchcraft-0.5.3}/docs/ROADMAP.md +0 -0
  48. {patchcraft-0.5.2 → patchcraft-0.5.3}/docs/STUDIES/2026-08-04-patch-techniques.md +0 -0
  49. {patchcraft-0.5.2 → patchcraft-0.5.3}/docs/design/2026-09-01-fase1-bugs-performance.md +0 -0
  50. {patchcraft-0.5.2 → patchcraft-0.5.3}/docs/design/2026-09-01-fase2-acelerador.md +0 -0
  51. {patchcraft-0.5.2 → patchcraft-0.5.3}/docs/design/2026-09-01-fase3-predicado.md +0 -0
  52. {patchcraft-0.5.2 → patchcraft-0.5.3}/setup.cfg +0 -0
  53. {patchcraft-0.5.2 → patchcraft-0.5.3}/setup.py +0 -0
  54. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft/__init__.py +0 -0
  55. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft/_accel.py +0 -0
  56. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft/_foldgeom.py +0 -0
  57. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft/cache.py +0 -0
  58. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft/extract.py +0 -0
  59. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft/geometry.py +0 -0
  60. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft/metrics.py +0 -0
  61. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft/pair.py +0 -0
  62. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft/py.typed +0 -0
  63. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft/resize.py +0 -0
  64. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft.egg-info/dependency_links.txt +0 -0
  65. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft.egg-info/requires.txt +0 -0
  66. {patchcraft-0.5.2 → patchcraft-0.5.3}/src/patchcraft.egg-info/top_level.txt +0 -0
  67. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/__init__.py +0 -0
  68. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/_datasets.py +0 -0
  69. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/_rng.py +0 -0
  70. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/conftest.py +0 -0
  71. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/test_accel.py +0 -0
  72. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/test_cache.py +0 -0
  73. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/test_datasets_helper.py +0 -0
  74. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/test_exactness.py +0 -0
  75. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/test_extract.py +0 -0
  76. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/test_geometry.py +0 -0
  77. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/test_import.py +0 -0
  78. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/test_metrics.py +0 -0
  79. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/test_pair.py +0 -0
  80. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/test_public_api.py +0 -0
  81. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/test_reference.py +0 -0
  82. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/test_resize.py +0 -0
  83. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/test_rng.py +0 -0
  84. {patchcraft-0.5.2 → patchcraft-0.5.3}/tests/test_stitch.py +0 -0
  85. {patchcraft-0.5.2 → patchcraft-0.5.3}/tools/benchmark.py +0 -0
  86. {patchcraft-0.5.2 → patchcraft-0.5.3}/uv.lock +0 -0
@@ -0,0 +1,45 @@
1
+ # Runs the gates CI runs, before the commit rather than after it.
2
+ #
3
+ # Deliberately absent: `ruff-format`. This project has never used it and
4
+ # adopting it here would reformat 36 of 65 files in a commit that changed no
5
+ # behaviour. `ruff check` is the gate; formatting is not.
6
+ #
7
+ # `mypy` runs through the project environment rather than through a pre-commit
8
+ # virtualenv, because it needs torch to resolve any annotation in `src/` and
9
+ # installing torch into an isolated hook environment would cost hundreds of
10
+ # megabytes per hook update.
11
+ #
12
+ # Install: see the "Run tests" section of CONTRIBUTING.md. On a machine with a
13
+ # global `core.hooksPath`, `pre-commit install` writes to `.git/hooks/`, which
14
+ # git then ignores, and the hook is silently inert. CONTRIBUTING says how to
15
+ # check.
16
+
17
+ repos:
18
+ - repo: https://github.com/pre-commit/pre-commit-hooks
19
+ rev: v6.0.0
20
+ hooks:
21
+ - id: check-added-large-files
22
+ args: ["--maxkb=512"]
23
+ - id: check-merge-conflict
24
+ - id: check-toml
25
+ - id: check-yaml
26
+ - id: end-of-file-fixer
27
+ - id: trailing-whitespace
28
+ - id: mixed-line-ending
29
+ args: ["--fix=no"]
30
+
31
+ - repo: https://github.com/astral-sh/ruff-pre-commit
32
+ rev: v0.16.5
33
+ hooks:
34
+ - id: ruff-check
35
+ args: ["src", "tests", "tools"]
36
+ pass_filenames: false
37
+
38
+ - repo: local
39
+ hooks:
40
+ - id: mypy-strict
41
+ name: mypy --strict src
42
+ entry: mypy --strict src
43
+ language: system
44
+ types: [python]
45
+ pass_filenames: false
@@ -0,0 +1,137 @@
1
+ # PatchCraft: the canonical guide
2
+
3
+ > **Single source, agent-agnostic.** The branded files ([`CLAUDE.md`](CLAUDE.md))
4
+ > point here and do not duplicate content. On any disagreement, this file wins.
5
+ >
6
+ > **Route**: this guide = *the rules* · [`MAP.md`](MAP.md) = *where things are* ·
7
+ > [`CONTRIBUTING.md`](CONTRIBUTING.md) = *how to run the gates and release* ·
8
+ > [`STATUS.md`](STATUS.md) = *where things stand now* ·
9
+ > [`docs/FOCO-1.0.md`](docs/FOCO-1.0.md) = *what is left before 1.0*.
10
+ > Do not repeat between them. Link.
11
+
12
+ ## 0. The invariants
13
+
14
+ Each of these was earned. Where one names an incident, the incident is real and
15
+ is in the changelog.
16
+
17
+ ### I1. Measure before you write it down
18
+
19
+ Every number in this repository has a command that reproduces it. A sentence
20
+ that says "faster", "exact" or "negligible" without one is a defect waiting to
21
+ be found, and several have been.
22
+
23
+ The failures this rule comes from: a predicate published as `k_max <= 4`,
24
+ measured false and retracted in public; a benchmark that reported the
25
+ byte-native path as nine times slower because `torch`'s `uint8.sum()` silently
26
+ promotes to `int64`, so it measured the opposite of what it claimed; a security
27
+ note asserting `Cache` runs `torch.load`, which it does not.
28
+
29
+ Recalling is not measuring. Reading the code is not measuring either, when the
30
+ question is numeric.
31
+
32
+ ### I2. `docs/THEORY.md` §9 is the arbiter
33
+
34
+ It says so itself. When two documents disagree about what a function accepts,
35
+ rejects or returns, §9 is the truth and the other document is the bug. When §9
36
+ and the code disagree, decide which is wrong before changing either, and say
37
+ which in the commit.
38
+
39
+ §9 has been wrong twice. That is not a reason to trust it less; it is why the
40
+ rule exists.
41
+
42
+ ### I3. A fix ships with a test that fails on the old code
43
+
44
+ Without one it is not a fix, it is a coincidence. The suite already contains
45
+ several tests whose only job is to prevent a specific defect from returning,
46
+ and each names the measurement in its docstring.
47
+
48
+ ### I4. The exactness suite exists to falsify, not to confirm
49
+
50
+ `tests/test_exactness.py` enumerates the legal geometry space independently of
51
+ the predicate and hunts for both counterexamples: a case inside the rule that
52
+ is not exact, and a case outside it that is exact by luck. If you change the
53
+ predicate, the enumerator must not learn about the change.
54
+
55
+ ### I5. The public surface is 20 names, frozen by test
56
+
57
+ `tests/test_public_api.py` pins the names, their signatures and the fields of
58
+ the four data carriers. Adding a name is a decision recorded in an ADR, never a
59
+ convenience taken mid-task. [`docs/FOCO-1.0.md`](docs/FOCO-1.0.md) says what
60
+ 1.0 freezes.
61
+
62
+ ### I6. Both code paths stay green
63
+
64
+ The library has a pure-torch path and, on five of six wheels, a Rust one. They
65
+ are bit-identical by test. Any change to the fold, the count map or the
66
+ accelerator must run both: `pytest -m "not gpu"` with the extension built, and
67
+ again with `PATCHCRAFT_ACCEL=0`.
68
+
69
+ ### I7. The version answers one question
70
+
71
+ Not "did a returned value change", because a fix changes values by definition.
72
+ The question is whether the **contract moved** (`0.Y.0`) or the
73
+ **implementation stopped violating it** (`0.y.Z`). The rule and its four worked
74
+ examples are in [`CONTRIBUTING.md`](CONTRIBUTING.md), and getting it wrong is
75
+ what nearly shipped 0.5.2 as 0.6.0.
76
+
77
+ ### I8. Nothing ephemeral lives in the project folder
78
+
79
+ Caches, build output and virtual environments are redirected by user-level
80
+ environment variables. `accel/target` reached 269 MiB inside the tree before
81
+ this was enforced. Never commit a machine path to fix it: that mistake is
82
+ already in the history, as a `cache_dir = "Z:\caches\pytest"` that created a
83
+ literal directory of that name on Linux.
84
+
85
+ ### I9. A tracked file ships to PyPI unless two files say otherwise
86
+
87
+ `setuptools-scm`'s file finder offers every git-tracked file to the sdist, so
88
+ `MANIFEST.in` is a blocklist over everything git knows about, and
89
+ `tools/check_dist.py` holds the matching allowlist. Adding a top-level
90
+ directory means updating both. The gate will tell you; do not silence it.
91
+
92
+ ### I10. Do not invent what already exists
93
+
94
+ Check for an official or established solution before writing one. The
95
+ accelerator's packaging, the versioning rule, the citation format and the
96
+ outreach layout were all taken from prior art rather than designed here, and
97
+ each is better for it. Writing something new is fine when nothing fits; writing
98
+ it without looking is not.
99
+
100
+ ## 1. Writing
101
+
102
+ Prose in this repository has no em-dashes. Replace the semantic role with a
103
+ sentence that carries it, never with another symbol. Keep articles and
104
+ connectives; one idea per paragraph. `README.md` is a call page, not a manual:
105
+ the manual is [`docs/GUIDE.md`](docs/GUIDE.md), and complex examples belong
106
+ there.
107
+
108
+ Documents that state a version go stale. There is exactly one version string
109
+ left in the documentation, the BibTeX entry in GUIDE section 9, plus
110
+ [`CITATION.cff`](CITATION.cff). Do not add a third.
111
+
112
+ ## 2. Before you touch anything
113
+
114
+ ```
115
+ uv sync --locked --extra cache --extra dev
116
+ ruff check src tests tools
117
+ mypy --strict src
118
+ pytest -m "not gpu"
119
+ ```
120
+
121
+ For a change to the fold, the predicate or the accelerator, add:
122
+
123
+ ```
124
+ PATCHCRAFT_SWEEP_FULL=1 pytest tests/test_exactness.py
125
+ cargo test --manifest-path accel/Cargo.toml
126
+ ```
127
+
128
+ The same gates run as a pre-commit hook. On a machine with a global
129
+ `core.hooksPath`, that hook is installed but inert;
130
+ [`CONTRIBUTING.md`](CONTRIBUTING.md) says how to tell.
131
+
132
+ ## 3. What this project is not
133
+
134
+ No batch axis, no dataset, no dataloader, no model, no padding to make an
135
+ awkward geometry fit. Those are out by decision and the line is drawn in
136
+ [`docs/SCOPE.md`](docs/SCOPE.md) and [`docs/THEORY.md`](docs/THEORY.md) §0.
137
+ A request to add one of them is a scope conversation, not a feature.
@@ -6,6 +6,111 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.5.3] - 2026-09-03
10
+
11
+ ### Added
12
+ - `docs/STUDIES/2026-09-03-strata-conformity.md`, a review of this repository
13
+ against Strata v1.2.2. Its §9 bounded the review before it bounded the
14
+ project: name the genre and apply its standard, and take no change as the
15
+ default verdict unless a defect pays for the fix. The genre is a library, so
16
+ the standard is tests, packaging and CI. The finding is conformance, one gap
17
+ and one piece of debris. The gap is the absence of a DOI, which is the only
18
+ item missing from the publishing pattern and cannot be applied to releases
19
+ already out. The debris was a stale `.pytest_cache`, removed, with the
20
+ redirect verified to hold.
21
+
22
+
23
+
24
+ - `STATUS.md`, the snapshot: what is released, what is unreleased, and who each
25
+ open item waits on. Writing it surfaced that `docs/FOCO-1.0.md` still listed
26
+ B1, B2, B3 and B6 as open although all four were closed in 0.5.0, the same
27
+ bookkeeping gap B5 had. Each is now recorded with what closed it, verified
28
+ rather than assumed: the retracted predicate survives only in the phase spec
29
+ and in the blocker's own text, the falsification suite enumerates 126,736
30
+ geometries, the surface is pinned with `inspect.signature`, and `tilings`
31
+ returns 73 specs with one whole-image entry and none labelled `overlap`.
32
+ B4 is the only blocker still open.
33
+ - `AGENTS.md`, the canonical agent-facing guide, and a thin `CLAUDE.md` that
34
+ points at it rather than duplicating it. Its ten invariants are the ones this
35
+ repository actually enforces, and each names the incident it came from: the
36
+ predicate published and retracted, the benchmark that measured the opposite
37
+ of what it claimed because `uint8.sum()` promotes silently, the 269 MiB of
38
+ Rust build output that lived in the tree, and the machine path once committed
39
+ to `pyproject.toml` that created a literal `Z:\caches\pytest` directory on
40
+ Linux. `AGENTS.md` ships in the sdist; `CLAUDE.md` does not, having no reader
41
+ there.
42
+ - Issue forms and a pull-request template. A report about this library is
43
+ irreproducible without the geometry, the dtype, the torch version and
44
+ `accel_available()`, so the forms make those required rather than suggested,
45
+ and every bug form asks whether the behaviour survives `PATCHCRAFT_ACCEL=0`,
46
+ which is the one answer that separates the native kernel from the library.
47
+ "Wrong numeric result" is a form of its own, because a wrong number is a
48
+ first-class defect here. The PR template asks whether a returned value moved
49
+ and offers the four answers the versioning rule distinguishes.
50
+ - `SECURITY.md`. It names the real edges instead of being boilerplate: the
51
+ native kernel writes through raw pointers and trusts the geometry the Python
52
+ side validated, which is the sharpest thing in the library; `Cache` joins its
53
+ `namespace` straight into a path, so a namespace derived from untrusted input
54
+ escapes the root; nothing in the shipped package opens a network connection;
55
+ and image decoding happens in Pillow before this library sees anything. It
56
+ also says what is not a security issue here, because a wrong numeric result
57
+ belongs in a public issue with its measurement.
58
+ - `CODE_OF_CONDUCT.md`, Contributor Covenant 2.1, taken verbatim from the
59
+ source repository rather than retyped.
60
+ - `MAP.md`, one page of wayfinding: an annotated tree of the repository and a
61
+ want-to/go-to table. It follows the shape the sibling projects use, and it
62
+ opens by saying what the repository actually is, which is 20 public names
63
+ over eight modules plus the evidence that they behave as documented. Every
64
+ link and anchor in it is checked, and every path it names exists.
65
+ - `.pre-commit-config.yaml`, running the gates CI runs. `ruff-format` is
66
+ deliberately absent: the project has never used it and adopting it would
67
+ reformat 36 of 65 files in a commit that changed no behaviour. Two things
68
+ surfaced while wiring it up. `docs/FOCO-1.0.md` had no trailing newline, now
69
+ fixed with the file's own CRLF rather than a bare LF, which is a mistake the
70
+ `mixed-line-ending` hook caught on the first attempt. And on a machine with a
71
+ global `core.hooksPath`, `pre-commit install` writes a hook git then ignores,
72
+ so it is inert without saying so; `CONTRIBUTING.md` gives the one command
73
+ that tells you which case you are in.
74
+ - `CITATION.cff`, validated against Citation File Format 1.2.0, so GitHub shows
75
+ the "Cite this repository" button and the metadata has one authoritative
76
+ home. It ships in the sdist. The BibTeX entry in GUIDE section 9 stays,
77
+ because `cffconvert`'s generated form drops the version and emits a
78
+ placeholder key, but the `.cff` is now the source and the release checklist
79
+ moves both. A test pins its version against the newest released section of
80
+ this file, since comparing it to `patchcraft.__version__` cannot work in a
81
+ checkout, where setuptools-scm resolves a development version.
82
+
83
+ ### Changed
84
+ - **`docs/USAGE.md` is executed instead of transcribed, which closes the last
85
+ 1.0 blocker.** The page claimed eighteen public names when there were twenty
86
+ and had spent three releases describing an API that had moved, and the script
87
+ that once generated it was never tracked, so it could not be regenerated.
88
+ Making the page run in place is the durable fix: `tests/test_docs_usage.py`
89
+ collects it as a doctest, so a stale line fails CI rather than misleading a
90
+ reader. Only five outputs were genuinely wrong; the other eight failures were
91
+ formatting, where the output touched the closing fence and doctest read the
92
+ fence as expected text. `ELLIPSIS` and `NORMALIZE_WHITESPACE` are enabled in
93
+ `pyproject.toml` so the page may still elide a temp path or wrap a long repr
94
+ for the reader. The version literal is gone from it: the version comes from
95
+ the git tag, so any literal is wrong the moment it is written.
96
+
97
+ ### Fixed
98
+ - **THEORY §9 justified promoting `bfloat16` with a fact that is false of it,
99
+ and §9 is the arbiter of the contract.** Both half formats were promoted to
100
+ a `float32` accumulator citing `float16`'s finite maximum of 65504. Only
101
+ `float16` overflows there: on the documented case its numerator reaches
102
+ `inf` in 144 of 256 pixels, while `bfloat16` carries `float32`'s exponent
103
+ and peaked at `9.0112e+04` against a finite maximum of `3.390e+38`. What the
104
+ promotion buys `bfloat16` is precision, since it has 8 mantissa bits to
105
+ `float16`'s 11, and on ordinary [0, 1] data it cut the maximum error from
106
+ `9.262e-03` to `1.935e-03`. §9.2 now carries one entry per format with the
107
+ measurement behind each, and the two docstrings and GUIDE section 4 say the
108
+ same. Two tests pin it.
109
+ - Closed blocker B5 in `docs/FOCO-1.0.md`. The contradiction it named, §9.2
110
+ accepting the half-precision promotion in one list and excluding it as out
111
+ of scope eight lines below, had already been removed in 0.3.0 and never
112
+ recorded. Checking that is what surfaced the `bfloat16` error above.
113
+
9
114
  ## [0.5.2] - 2026-09-03
10
115
 
11
116
  ### Fixed
@@ -666,7 +771,8 @@ First public release. Public API stable; signatures will only change in 1.x.
666
771
  - [`README.md`](README.md) covers installation, the car-vs-track metaphor,
667
772
  validation lab.
668
773
 
669
- [Unreleased]: https://github.com/LeoPR/PatchCraft/compare/v0.5.2...HEAD
774
+ [Unreleased]: https://github.com/LeoPR/PatchCraft/compare/v0.5.3...HEAD
775
+ [0.5.3]: https://github.com/LeoPR/PatchCraft/releases/tag/v0.5.3
670
776
  [0.5.2]: https://github.com/LeoPR/PatchCraft/releases/tag/v0.5.2
671
777
  [0.5.1]: https://github.com/LeoPR/PatchCraft/releases/tag/v0.5.1
672
778
  [0.5.0]: https://github.com/LeoPR/PatchCraft/releases/tag/v0.5.0
@@ -0,0 +1,51 @@
1
+ cff-version: 1.2.0
2
+ title: "PatchCraft: image patch extraction, reconstruction, pairing and seam-aware stitching"
3
+ message: "If you use this software, please cite it using these metadata."
4
+ type: software
5
+ authors:
6
+ - given-names: Leonardo
7
+ family-names: Marques de Souza
8
+ email: leonardo.marques.souza@gmail.com
9
+ version: "0.5.3"
10
+ date-released: "2026-09-03"
11
+ repository-code: "https://github.com/LeoPR/PatchCraft"
12
+ license: MIT
13
+ abstract: >
14
+ PatchCraft cuts one image into patches and puts it back. One call takes one
15
+ (C, H, W) float tensor and returns one tensor: there is no batch axis, no
16
+ dataset, no dataloader and no model, and that boundary is documented as
17
+ binding rather than provisional. It owns the unfold and fold arithmetic, the
18
+ geometry validation and the seam blending, so a pipeline can own the rest.
19
+
20
+ Two silent defects motivate it. The intuitive reshape of F.unfold's output
21
+ gives the right shape with the wrong pixels, and a stride that does not cover
22
+ the image leaves pixels at zero without any error: on a 128 by 128 image with
23
+ patch 32 and stride 20, 3840 of 16384 pixels. Both are checked once here,
24
+ with tests around them.
25
+
26
+ The numeric contract is stated as a caller-evaluable predicate rather than a
27
+ promise. The extract and reconstruct round trip is bit-exact if and only if
28
+ every value of the overlap coverage map is a power of two, always true at
29
+ stride equal to patch size, and outside that rule the per-pixel error is
30
+ bounded by (k+1) times eps times the absolute value, with k the pixel's
31
+ coverage count. An earlier and weaker form of that claim was published, then
32
+ measured false and publicly retracted; a falsification suite now enumerates
33
+ the 126,736 legal geometries of the space independently of the predicate and
34
+ tries to break both halves of it.
35
+
36
+ Five of the six published wheels carry a Rust kernel for the overlapping
37
+ fold, compiled into the wheel itself rather than into a separate package, and
38
+ bit-identical to the pure-torch path by test. Every other platform installs a
39
+ universal wheel and runs the same operation in torch.
40
+
41
+ Pre-1.0: output values may change when the middle digit moves, and the
42
+ changelog records each change with the measurement behind it.
43
+ keywords:
44
+ - image-processing
45
+ - patches
46
+ - super-resolution
47
+ - pytorch
48
+ - tiling
49
+ - reconstruction
50
+ - seam-blending
51
+ - numerical-reproducibility
@@ -0,0 +1,83 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
6
+
7
+ We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8
+
9
+ ## Our Standards
10
+
11
+ Examples of behavior that contributes to a positive environment for our community include:
12
+
13
+ * Demonstrating empathy and kindness toward other people
14
+ * Being respectful of differing opinions, viewpoints, and experiences
15
+ * Giving and gracefully accepting constructive feedback
16
+ * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
17
+ * Focusing on what is best not just for us as individuals, but for the overall community
18
+
19
+ Examples of unacceptable behavior include:
20
+
21
+ * The use of sexualized language or imagery, and sexual attention or advances of any kind
22
+ * Trolling, insulting or derogatory comments, and personal or political attacks
23
+ * Public or private harassment
24
+ * Publishing others' private information, such as a physical or email address, without their explicit permission
25
+ * Other conduct which could reasonably be considered inappropriate in a professional setting
26
+
27
+ ## Enforcement Responsibilities
28
+
29
+ Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
30
+
31
+ Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
32
+
33
+ ## Scope
34
+
35
+ This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
36
+
37
+ ## Enforcement
38
+
39
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at leonardo.marques.souza@gmail.com. All complaints will be reviewed and investigated promptly and fairly.
40
+
41
+ All community leaders are obligated to respect the privacy and security of the reporter of any incident.
42
+
43
+ ## Enforcement Guidelines
44
+
45
+ Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
46
+
47
+ ### 1. Correction
48
+
49
+ **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
50
+
51
+ **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
52
+
53
+ ### 2. Warning
54
+
55
+ **Community Impact**: A violation through a single incident or series of actions.
56
+
57
+ **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
58
+
59
+ ### 3. Temporary Ban
60
+
61
+ **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
62
+
63
+ **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
64
+
65
+ ### 4. Permanent Ban
66
+
67
+ **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
68
+
69
+ **Consequence**: A permanent ban from any sort of public interaction within the community.
70
+
71
+ ## Attribution
72
+
73
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
74
+
75
+ Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
76
+
77
+ For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations].
78
+
79
+ [homepage]: https://www.contributor-covenant.org
80
+ [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
81
+ [Mozilla CoC]: https://github.com/mozilla/diversity
82
+ [FAQ]: https://www.contributor-covenant.org/faq
83
+ [translations]: https://www.contributor-covenant.org/translations
@@ -13,6 +13,30 @@ does not install them:
13
13
  uv sync --locked --extra cache --extra dev
14
14
  ```
15
15
 
16
+ The same gates run before a commit if you install the hooks:
17
+
18
+ ```
19
+ uvx pre-commit install
20
+ uvx pre-commit run --all-files # once, to see the tree is clean
21
+ ```
22
+
23
+ **Check that the hook is actually live**, because on some machines it is not.
24
+ `pre-commit install` writes `.git/hooks/pre-commit`, and git ignores that file
25
+ entirely when a global `core.hooksPath` is set:
26
+
27
+ ```
28
+ git config --get core.hooksPath # empty means the hook will run
29
+ ```
30
+
31
+ If it prints a path, the hooks in it run instead of yours, and `pre-commit`
32
+ is silently inert here. Either make that global hook delegate, or set
33
+ `core.hooksPath` for this clone alone, remembering that doing so also disables
34
+ whatever else the global hooks were doing.
35
+
36
+ `ruff-format` is deliberately not among the hooks. This project has never used
37
+ it, and adopting it would reformat 36 of 65 files in a commit that changed no
38
+ behaviour.
39
+
16
40
  ```
17
41
  pytest
18
42
  pytest -m "not gpu" # skip GPU-requiring tests
@@ -111,11 +135,22 @@ PatchCraft/
111
135
  ├── README.pt-BR.md the same call page in Portuguese
112
136
  ├── README.pypi.md the PyPI page (long_description), links absolute
113
137
  ├── CONTRIBUTING.md this file
138
+ ├── MAP.md one-page wayfinding: the tree, and a want-to/go-to table
139
+ ├── AGENTS.md the canonical rules; ten invariants, each earned
140
+ ├── CLAUDE.md thin pointer to AGENTS.md plus tool-specific notes
141
+ ├── STATUS.md the snapshot; what is in flight and what waits on whom
114
142
  ├── CHANGELOG.md Keep-a-Changelog format
115
143
  ├── LICENSE MIT
116
144
  ├── .python-version 3.13
117
145
  ├── .gitignore ignores archive/, venvs, caches, outputs
118
- ├── .github/workflows/
146
+ ├── .pre-commit-config.yaml the CI gates, before the commit instead of after
147
+ ├── CITATION.cff citation metadata; GitHub reads it for the cite button
148
+ ├── SECURITY.md how to report, and what the real attack surface is
149
+ ├── CODE_OF_CONDUCT.md Contributor Covenant 2.1, verbatim
150
+ ├── .github/
151
+ │ ├── ISSUE_TEMPLATE/ five forms; the required fields are what makes a report reproducible
152
+ │ ├── PULL_REQUEST_TEMPLATE.md asks whether a returned value moved, which decides the version bump
153
+ │ └── workflows/
119
154
  │ ├── test.yml matrix CI on PRs/main, plus the accelerated job
120
155
  │ └── release.yml publishes to PyPI on vX.Y.Z tag push (Trusted Publishing)
121
156
  ├── src/patchcraft/ library core, one-image-at-a-time primitives
@@ -159,7 +194,7 @@ PatchCraft/
159
194
  ├── docs/
160
195
  │ ├── GUIDE.md the manual, every README claim measured with its output
161
196
  │ ├── PERFORMANCE.md what the accelerator is worth, and how to re-measure
162
- │ ├── USAGE.md REPL walkthrough of every public symbol (behind, see B4)
197
+ │ ├── USAGE.md REPL walkthrough of every public symbol; run as a doctest
163
198
  │ ├── SCOPE.md responsibilities matrix + parallelization analysis
164
199
  │ ├── AUXILIARY.md tests/_datasets, lab/, Z:\ conventions (NOT part of the wheel)
165
200
  │ ├── THEORY.md distilled design + §9 condition contract; §0 binding scope
@@ -306,7 +341,7 @@ silent: a version that walks backwards rather than an error.
306
341
  ## Releasing (maintainer only)
307
342
 
308
343
  1. Close the `[Unreleased]` section in [`CHANGELOG.md`](CHANGELOG.md) as `[X.Y.Z] YYYY-MM-DD`.
309
- 2. Update the `version` field of the BibTeX entry in [`docs/GUIDE.md`](docs/GUIDE.md) section 9. It is the one version string left in the documentation, because a citation needs a concrete one.
344
+ 2. Update `version` and `date-released` in [`CITATION.cff`](CITATION.cff), and the `version` field of the BibTeX entry in [`docs/GUIDE.md`](docs/GUIDE.md) section 9. Those are the only version strings left in the repository, because a citation needs a concrete one. `uvx --from cffconvert cffconvert --validate` checks the file.
310
345
  3. Update [`docs/ROADMAP.md`](docs/ROADMAP.md) milestone checkboxes.
311
346
  4. Commit: `release: vX.Y.Z`.
312
347
  5. Tag + push: `git tag -a vX.Y.Z -m "..."` then `git push origin vX.Y.Z`.
@@ -17,10 +17,15 @@
17
17
  # `tools/check_dist.py` holds the matching allowlist and will fail the release
18
18
  # for anything unexpected, which is the reminder to come back here.
19
19
 
20
- include LICENSE
21
- include CHANGELOG.md CONTRIBUTING.md
20
+ include LICENSE CITATION.cff SECURITY.md CODE_OF_CONDUCT.md
21
+ include CHANGELOG.md CONTRIBUTING.md MAP.md AGENTS.md STATUS.md
22
+ # CLAUDE.md is tool-specific and points at AGENTS.md; it has no reader in an
23
+ # sdist. `exclude`, not `prune`: prune takes a directory and is a silent no-op
24
+ # on a file, which is how this shipped once.
25
+ exclude CLAUDE.md
22
26
  include README.md README.pt-BR.md README.pypi.md
23
27
  include pyproject.toml setup.py uv.lock .python-version
28
+ include .pre-commit-config.yaml
24
29
 
25
30
  recursive-include src *.py
26
31
  include src/patchcraft/py.typed
@@ -0,0 +1,107 @@
1
+ # MAP: finding your way around PatchCraft
2
+
3
+ > One page. If you know **what** you want, this says **where** it is.
4
+ > If you do not know yet, start at [README.md](README.md), which is the call page.
5
+
6
+ PatchCraft cuts one image into patches and puts it back. The whole library is
7
+ 20 public names over eight modules; almost everything in this repository is the
8
+ evidence that those 20 behave as documented.
9
+
10
+ ## The tree
11
+
12
+ ```
13
+ PatchCraft/
14
+ ├── README.md ................. the call page, canonical, English
15
+ │ README.pt-BR.md ........... the same page in Portuguese
16
+ │ README.pypi.md ............ the PyPI page; links absolute, because relative ones break there
17
+ ├── MAP.md .................... this file
18
+ ├── AGENTS.md ................. the rules, agent-agnostic and canonical; ten invariants
19
+ ├── CLAUDE.md ................. thin; points at AGENTS.md and holds tool-specific notes
20
+ ├── STATUS.md ................. the snapshot: where things are now, and who each item waits on
21
+ ├── CHANGELOG.md .............. every release, each change with the measurement behind it
22
+ ├── CONTRIBUTING.md ........... layout, the two code paths, the versioning rule, releasing
23
+ ├── CITATION.cff .............. citation metadata; GitHub reads it for the cite button
24
+ ├── LICENSE ................... MIT
25
+ ├── SECURITY.md ............... how to report, and the three trust boundaries that matter
26
+ ├── CODE_OF_CONDUCT.md ........ Contributor Covenant 2.1
27
+ │
28
+ ├── src/patchcraft/ ........... THE LIBRARY. 20 public names, frozen by test
29
+ │ ├── __init__.py ........... re-exports the surface and the generated version
30
+ │ ├── extract.py ............ image -> patches; Patchify wraps it for a Compose
31
+ │ ├── reconstruct.py ........ patches -> image, the exact inverse for untouched patches
32
+ │ ├── stitch.py ............. patches -> image with a window, for patches a model rewrote
33
+ │ ├── geometry.py ........... num_patches, tilings, scale_factor, paired_tilings; no pixels
34
+ │ ├── pair.py ............... LR/HR pairing; PatchPair, PatchMeta
35
+ │ ├── metrics.py ............ patch_metrics, per_patch_mse, per_patch_psnr
36
+ │ ├── resize.py ............. PIL and torch backends
37
+ │ ├── cache.py .............. content-addressed disk cache
38
+ │ ├── _foldgeom.py .......... the coverage guard both reconstruct and stitch call
39
+ │ └── _accel.py ............. bridge to the native kernel; falls back silently
40
+ │
41
+ ├── accel/ .................... the Rust kernel, compiled INTO the wheel, not a second package
42
+ │ └── src/kernel.rs ......... the gather-fold; cargo-tested without Python
43
+ │
44
+ ├── tests/ .................... the contract, executable
45
+ │ ├── test_exactness.py ..... enumerates 126,736 geometries and tries to FALSIFY the predicate
46
+ │ ├── test_reference.py ..... a naive loop the fast paths must agree with
47
+ │ ├── test_public_api.py .... freezes the 20 names, their signatures and carrier fields
48
+ │ └── _rng.py ............... audited data generation; integer ramps hid a real defect once
49
+ │
50
+ ├── docs/
51
+ │ ├── GUIDE.md .............. THE MANUAL. every README claim, measured, with its output
52
+ │ ├── THEORY.md ............. the math, and §9 is the arbiter of the per-function contract
53
+ │ ├── SCOPE.md .............. the line between this library and your pipeline
54
+ │ ├── PERFORMANCE.md ........ what the accelerator is worth; machine, versions, date, command
55
+ │ ├── USAGE.md .............. the 20 symbols one at a time; every >>> runs in the suite
56
+ │ ├── AUXILIARY.md .......... test fixtures, lab/, the off-tree conventions
57
+ │ ├── ROADMAP.md ............ milestones, historical
58
+ │ ├── FOCO-1.0.md ........... what 1.0 freezes, and the blockers still in the way
59
+ │ ├── ADR/ .................. one file per decision that shaped the API
60
+ │ ├── design/ ............... one spec per work phase: alternatives measured, then chosen
61
+ │ └── STUDIES/ .............. background reading, and the Strata conformity review
62
+ │
63
+ ├── tools/
64
+ │ ├── benchmark.py .......... accelerated vs pure, and proves they agree before timing
65
+ │ └── check_dist.py ......... release gate: extension present, versions agree, tag respected
66
+ │
67
+ ├── outreach/ ................. material for presenting the project; not shipped
68
+ ├── lab/ ...................... scratch experiments; only the README is tracked
69
+ └── .github/
70
+ ├── ISSUE_TEMPLATE/ ....... five forms, each asking for the geometry and PATCHCRAFT_ACCEL=0
71
+ ├── PULL_REQUEST_TEMPLATE.md
72
+ └── workflows/ ............ test.yml (both code paths) and release.yml (one project, six wheels)
73
+ ```
74
+
75
+ ## I want to... go here
76
+
77
+ | I want to | Go to |
78
+ |---|---|
79
+ | **See what this is, in two minutes** | [README.md](README.md) |
80
+ | **Work on it as an agent**, or learn what this repo insists on | [AGENTS.md](AGENTS.md) |
81
+ | **Use it**: every argument, measured, with its printed output | [docs/GUIDE.md](docs/GUIDE.md) |
82
+ | Know **when the round trip is exact**, as a rule I can evaluate myself | [docs/GUIDE.md §4](docs/GUIDE.md#4-when-the-round-trip-is-bit-for-bit), and [docs/ADR/0003](docs/ADR/0003-reversibility-classes.md) for the contract |
83
+ | Know **what each function accepts and rejects**, precisely | [docs/THEORY.md §9](docs/THEORY.md), which is the arbiter where documents disagree |
84
+ | Know **whether this library is the right tool** | [docs/SCOPE.md](docs/SCOPE.md) |
85
+ | Know **what the native accelerator is worth**, and re-measure it | [docs/PERFORMANCE.md](docs/PERFORMANCE.md), then `python tools/benchmark.py` |
86
+ | Know **what the project refuses to claim** | [docs/GUIDE.md §8](docs/GUIDE.md#8-what-this-project-does-not-claim) |
87
+ | Understand **why the API looks like this** | [docs/ADR/](docs/ADR/) |
88
+ | Understand **why a phase was built the way it was** | [docs/design/](docs/design/) |
89
+ | **Contribute**: run the gates, understand the two code paths | [CONTRIBUTING.md](CONTRIBUTING.md) |
90
+ | **Release** it | [CONTRIBUTING.md](CONTRIBUTING.md), the "Releasing" section |
91
+ | Know **what changed and why** | [CHANGELOG.md](CHANGELOG.md) |
92
+ | Know **what is left before 1.0** | [docs/FOCO-1.0.md](docs/FOCO-1.0.md) |
93
+ | Know **where things stand right now** | [STATUS.md](STATUS.md) |
94
+ | **Cite** it | [CITATION.cff](CITATION.cff) |
95
+ | Report something **exploitable**, or read the trust boundaries | [SECURITY.md](SECURITY.md) |
96
+ | **Report a bug** or propose a change | the issue forms, or [CONTRIBUTING.md](CONTRIBUTING.md) to send a PR |
97
+
98
+ ## Two things worth knowing before you read anything else
99
+
100
+ **The contract is falsifiable, on purpose.** `tests/test_exactness.py` exists to
101
+ break the library's own exactness claim, not to confirm it. That claim was
102
+ published in a weaker form, measured false and retracted, and the suite is what
103
+ came out of it. When a document and a test disagree, the test is right.
104
+
105
+ **Where documents disagree with each other, [THEORY.md](docs/THEORY.md) §9
106
+ wins.** It says so itself, and it is the section to fix first when something is
107
+ found wrong.