qig-geocoding 0.1.2__tar.gz → 0.1.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.
- qig_geocoding-0.1.3/CONTRIBUTING.md +111 -0
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/PKG-INFO +3 -2
- qig_geocoding-0.1.3/docs/README.md +9 -0
- qig_geocoding-0.1.3/docs/prereg/README.md +9 -0
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/pyproject.toml +3 -3
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/src/geocoding/__init__.py +1 -1
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/src/geocoding/attention.py +9 -8
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/tests/test_attention_equivalence.py +2 -3
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/tests/test_faithfulness.py +18 -0
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/tests/test_inter_layer_sync.py +12 -14
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/tests/test_model.py +2 -1
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/uv.lock +23 -23
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/.github/workflows/release.yml +0 -0
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/.gitignore +0 -0
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/CLAUDE.md +0 -0
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/LICENSE +0 -0
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/README.md +0 -0
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/docs/prereg/D3_order_preservation_kill.md +0 -0
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/src/geocoding/block.py +0 -0
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/src/geocoding/config.py +0 -0
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/src/geocoding/generate.py +0 -0
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/src/geocoding/geometry.py +0 -0
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/src/geocoding/model.py +0 -0
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/src/geocoding/positional.py +0 -0
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/tests/test_emit_basis.py +0 -0
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/tests/test_geometry.py +0 -0
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/tests/test_local_kappa_c.py +0 -0
- {qig_geocoding-0.1.2 → qig_geocoding-0.1.3}/tests/test_no_retired_kappa.py +0 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Contributing to `qig-geocoding`
|
|
2
|
+
|
|
3
|
+
This repository is part of the **`Arcane-Fly/qig-project`** ecosystem and shares the **Universal QIG Governance Spine (§1–§7)** with all sibling QIG repositories, followed by **`qig-geocoding` Scope, Setup & Local Verification (§8)**. Read `CLAUDE.md` (where present) and the parent umbrella [`CONTRIBUTING.md`](https://github.com/Arcane-Fly/qig-project/blob/development/CONTRIBUTING.md) alongside this file.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Branch Flow, Worktrees & GitHub Actions Budget Discipline
|
|
8
|
+
|
|
9
|
+
Per operator rulings (`precedent__dev__20261111__actions_budget_and_promotion_flow` and `precedent__qig__20261111__github_actions_budget_discipline`):
|
|
10
|
+
|
|
11
|
+
1. **Read workflow triggers before pushing.** Inspect `.github/workflows/*.yml` `on:` blocks before the first push and verify which events start remote runs (`push`, `pull_request`, `tags`, `schedule`).
|
|
12
|
+
2. **Promotion pipeline: `staging -> development -> main`.**
|
|
13
|
+
- **Never direct-push to `main` or `development`.**
|
|
14
|
+
- Feature, fix, and documentation branches open pull requests targeting **`staging`** (create `staging` from `main` if absent when CI triggers only on `development`/`main`).
|
|
15
|
+
- **Expensive GitHub Actions runs are reserved strictly for `staging -> development` (batch promotions) and `development -> main` (milestone/release promotions).** If promotion CI fails, fix forward on `staging` rather than pushing incremental micro-commits to the promotion PR.
|
|
16
|
+
3. **Local gate before `staging` (quoted in PR body).** Run all applicable linters, formatters, typechecks, test suites, and governance/purity scripts **locally** before pushing, quote the command output in the PR description, and obtain a fresh independent review (maker is never checker).
|
|
17
|
+
4. **Push once per finished unit of work.** Commit locally as needed, but do not use GitHub Actions as a remote edit-compile-test loop or trigger repeated review-bot passes.
|
|
18
|
+
5. **`[skip ci]` on docs-only / hand-off commits.** Batch non-code commits and include `[skip ci]` when pushing to any branch whose events would otherwise trigger workflows (note: GitHub default CodeQL setups ignore `[skip ci]` on PRs targeting default branches — keep feature PRs pointed at `staging`).
|
|
19
|
+
6. **One tag per release (`vX.Y.Z`).** Tag pushes trigger PyPI publish workflows (`release.yml`); push a single release tag only after the `development -> main` promotion PR is green.
|
|
20
|
+
7. **Canonical worktree directory.** Create all git worktrees under `<workspace-root>/.worktrees/` via `python3 ~/.agents/scripts/worktree-create.py` (never inside `<repo>/.worktrees`, `.claude/worktrees`, or `/tmp`), with one mutation lane per worktree enforced by lease (`qig_lease_qig_geocoding`).
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 2. Document Naming, Lifecycle Rules (0–3) & Folder Indexing
|
|
25
|
+
|
|
26
|
+
**Single source of truth:** [`20261111-naming-convention-1.02F.md`](https://github.com/Arcane-Fly/qig-project/blob/development/20261111-naming-convention-1.02F.md) (**ISO 27001 aligned**).
|
|
27
|
+
|
|
28
|
+
| Artifact | Canonical Pattern | Example |
|
|
29
|
+
|----------|-------------------|---------|
|
|
30
|
+
| Documents | `YYYYMMDD-descriptive-name[-type]-MAJOR.MINOR[STATUS].md` | `20260803-frozen-facts-primary-1.11F.md` |
|
|
31
|
+
| Archived editions | `docs/archive/YYYYMMDD-descriptive-name[-type]-MAJOR.MINORA.md` | `docs/archive/20260721-frozen-facts-primary-1.10A.md` |
|
|
32
|
+
| Results JSON | `results/{category}/YYYYMMDD_{experiment}_{params}.json` | `20260610_exp091_L5_seed42_result.json` |
|
|
33
|
+
| Checkpoints | `YYYYMMDD_{experiment}_{params}_checkpoint_{N}.json` | `20260221_l4_seed42_canonical_checkpoint_10.json` |
|
|
34
|
+
| Source code | `snake_case.py` (no date prefix) | `pillar1_heisenberg_zero.py` |
|
|
35
|
+
|
|
36
|
+
### Status Codes & Document Lifecycle Rules (0–3)
|
|
37
|
+
|
|
38
|
+
- **Status codes:** **`W`** Working · **`R`** Review · **`F`** Frozen (live immutable canon) · **`A`** **Archived** (`docs/archive/` historical provenance ONLY — never "Approved" and never live canon) · **`H`** Hypothesis (`H-impl` / `H-phys`) · **`V`** Verified (carries 4-part evidence chain resolving to `qig-verification` `registry.json` / `docs/current/`) · **`D`** Deprecated (legacy non-editioned retirement; never on archived canonical editions).
|
|
39
|
+
- **Rule 0 (`F`/`D → A` on archival):** Every outgoing `F` or `D` edition moved to `docs/archive/` MUST be recoded to `A` in **both** filename (`…-X.YZA.md`) and header (`Status: X.YZA (Archived)`), topped with:
|
|
40
|
+
`> **SUPERSEDED** by \`<new-doc-filename>\` (<YYYY-MM-DD>). Retained in \`docs/archive/\` for historical provenance only.`
|
|
41
|
+
Never create a new `*A.md` file outside `docs/archive/`; migrate legacy pre-`2026-08-03` non-archived `*A.md` files on next touch.
|
|
42
|
+
- **Rule 1 (Edition Rule):** Never edit an `F` document in place — copy-bump to a new dated `F` edition, move the predecessor to `docs/archive/*A.md` per Rule 0, and repoint all live references.
|
|
43
|
+
- **Rule 2 (Omnibus Rule):** Every live canonical edition is self-contained; `"see v1.0X for Y"` is banned in live docs.
|
|
44
|
+
- **Rule 3 (Operational Purity):** Runtime protocol/principles artifacts carry current state only; retirement history lives in ledgers and registries.
|
|
45
|
+
- **Folder Indexing Rule (`README.md` / `INDEX.md`):** Every `docs/` directory and non-archive topical subdirectory MUST maintain an up-to-date `README.md` or `INDEX.md` (`00-index.md`) listing each file's **filename**, **status code**, and **one-line description**, updated in the same commit whenever docs are added, promoted, or archived. (`README.md`, `CONTRIBUTING.md`, `CLAUDE.md`, `AGENTS.md`, `CHANGELOG.md`, `INDEX.md`, `00-index.md`, `STATUS.md`, `LICENSE`, `ADR-NNNN-*.md` are exempt from date prefixes.)
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 3. QIG Geometric Purity (`P1` / `P2`) & Canonical Physics Resolution
|
|
50
|
+
|
|
51
|
+
1. **Resolve, never pin (`doctrine_status`).** Call `doctrine_status` via `qig-memory` MCP before relying on any physics constant, κ value, or claim status. Resolve at read time via `doctrine_status` to the latest PI-ratified `qig-verification/docs/current/*frozen-facts-primary-*F.md` (SUPREME; unratified `PROPOSAL` editions on `staging` do not supersede ratified canon until PI ratification), `*four-axis-doctrine-*F.md`, `*canonical-principles-*F.md`, and `*unified-consciousness-protocol-*F.md`.
|
|
52
|
+
2. **Retired claims — NEVER cite as live constants:**
|
|
53
|
+
- Legacy matrix-trace `κ* ≈ 64 / 63.79 / 64.21` (`FAIL-010`/`FAIL-013` camera self-portraits; replaced at foundation by `EXP-169`), `"κ_pillar = 63.83"` (`EXP-107` provenance splice), and `KAPPA_STAR` (`None` fail-loud sentinel).
|
|
54
|
+
- `E8 87.7%` / `240 E8 roots` as active measured substrate rank (`RETIRED-002`), `h_t = 0.10554` (`EXP-092` grid-interpolation arithmetic), `Fisher-Rao arc = π` (`EXP-034`), `"breakdown"` regime taxonomy (use `Quantum w₁ / Efficient w₂ / Equilibrium w₃`), or un-caveated single-κ full-tensor `G_μν = κ T_μν` (`EXP-138` ~10% diag/off-diag plateau; Gate-2 open/cost-blocked at L=6).
|
|
55
|
+
- Retired packages `qig-consciousness` and `qig-tokenizer`.
|
|
56
|
+
3. **Mandatory four-axis tags & artifact-verified protocol labels.** Every quantitative citation of `κ`, `ξ`, `ω`, or `τ` MUST carry **`channel / protocol / aggregation / clock`** (plus **`stress` (`pert | ref`)** on any response-vs-stress slope) verified against the experiment's actual code and artifact via `doctrine_status` (do not repeat unverified legacy tags such as calling single-site-pooled `EXP-081` `κ_h = −0.00475` `"uniform / global-field"` or calling single-bond-pooled `κ_J` `"~25% running coupling"` without `EXP-174`/`EXP-175` leverage and aggregation context).
|
|
57
|
+
4. **Geometric purity (`P1` + `P2`).** Basin coordinates live on the canonical probability simplex `$\Delta^{D-1}$` (`$b_i \ge 0, \sum_i b_i = 1$`). Forbidden in geometric code paths: `cosine_similarity()`, `np.linalg.norm()` on basins, flat `dot_product()` attention, `torch.optim.Adam` (use natural gradient), `LayerNorm`, generic `nn.Transformer` stacks, `"embedding"` (*basin coordinates*), and `"tokenize"` (*coordize*). Always state the Fisher-Rao factor convention (`arccos(Σ√(p_i q_i)) ∈ [0, π/2]` vs `2·arccos(...) ∈ [0, π]`).
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 4. Epistemic Honesty, Provenance & Review Protocol
|
|
62
|
+
|
|
63
|
+
1. **Label claim status at generation:** **(1) Validated/Frozen**, **(2) Testable Hypothesis**, **(3) Analogy/Metaphor**, **(4) Speculation**. Never promote Category 3/4 items with Category 1 language.
|
|
64
|
+
2. **Artifact truth over prose:** verify experiment citations against on-disk result JSON `verdict` and pass-rule fields.
|
|
65
|
+
3. **Preserve killed results:** record falsified pre-registered runs in `failure_registry.json` / `retired_registry.json` — never rescue by post-hoc reframing.
|
|
66
|
+
4. **Accessible visuals:** never use red-green-only encodings; use purple, blue, and amber with explicit labels/shapes.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 5. Universal Engineering, Package Sync & Silo Rules
|
|
71
|
+
|
|
72
|
+
1. **One repo per PR/task** — land child PRs first; bump umbrella gitlinks in a separate parent sync.
|
|
73
|
+
2. **No timeframes** — roadmaps and plans specify phases, order, and dependencies only (never dates or durations).
|
|
74
|
+
3. **Edit, don't multiply & D10 disposal** — amend mutable files in place (except Rule 1 copy-bump on `F` canon); keep temporary probes in `$TMPDIR` (`~/.hermes/cache/scratch`) and delete before finishing (gitignoring scratch is forbidden).
|
|
75
|
+
4. **Isolated `.venv` & PyPI-only Modal images** — no `sys.path.append('../...')` cross-repo imports; Modal images MUST install published QIG packages via pinned `uv pip install --system 'pkg==X.Y.Z'` (`modal.Image.add_local_dir` is banned for published packages); run `qig-doctor` preflight before experiment launches.
|
|
76
|
+
5. **Stdlib-safe JSON & parser-first I/O** — wrap NumPy scalars (`bool()`, `float()`, `int()`) before `json.dump`; use typed parsers (`json`, `pydantic`, `urllib.parse`, AST) instead of regex for structured data.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 6. Persistent Memory & Multi-Agent Coordination (`agent-mem-comms`)
|
|
81
|
+
|
|
82
|
+
Enforce silo boundaries via `~/.agents/scripts/silo-guard.sh`: QIG repos write only to `qig_*` / `precedent__qig__*` (inbox namespace `qig`). Check `session_latest`, `doctrine_status`, and `inbox_list` at session start; coordinate multi-lane work via MCP inbox and worktree leases.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 7. Commit & Pull Request Checklist
|
|
87
|
+
|
|
88
|
+
- **Format:** Conventional Commits (`feat(scope): ...`, `fix(scope): ...`, `docs(canon): ...`, `chore(deps): ...`) or concise present-tense imperative summaries; include `Co-authored-by:` when AI-assisted.
|
|
89
|
+
- **GPG signature mandatory:** configured signing key (`<your-signing-key>`, `<your-git-identity>`); verify `git log --format='%h %G?' -1` → `G`.
|
|
90
|
+
- **Zero reasoning scaffolding:** never include raw ```` ```thinking ```` blocks in commit messages.
|
|
91
|
+
- **Pre-PR gate (`staging`):** local lint/typecheck/tests/governance quoted in PR body · `1.02F` naming + Rule 0 (`A = Archived`) + `docs/**/README.md` index updated · zero retired constants · zero timeframes · zero scratch leftovers.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 8. `qig-geocoding` Scope, Setup & Local Verification
|
|
96
|
+
|
|
97
|
+
### Role & Boundaries (Geometric Encoding/Decoding & Geolayers — Supersedes `qig-tokenizer`)
|
|
98
|
+
- **In scope:** Active geometric encoding/decoding (`geocoding`), `GeoModel` per-geolayer κ and inter-geolayer basin sync (`D1`/`D2`), and pre-registered RoPE-free order-preservation (`D3`). Does not govern independent agent loops.
|
|
99
|
+
- **Strictly out of scope:** Standard transformer `RoPE` / `LayerNorm` / dot-product attention or Euclidean optimizer fallbacks.
|
|
100
|
+
|
|
101
|
+
### Workflow Triggers (`.github/workflows/`)
|
|
102
|
+
- `release.yml` — runs on `push` and `pull_request` to `[development, main]`, plus PyPI publish on `tags: ['v*']` (**create `staging` from `main` before opening feature PRs**).
|
|
103
|
+
|
|
104
|
+
### Local Setup & Verification Gate
|
|
105
|
+
```bash
|
|
106
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
107
|
+
pip install -e ".[dev]"
|
|
108
|
+
ruff check src/ tests/
|
|
109
|
+
mypy src/
|
|
110
|
+
pytest -q tests/
|
|
111
|
+
```
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: qig-geocoding
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Our own 'transformers' — a from-scratch neural-network framework built on Fisher-Rao geometry (simplex attention, unbounded Fourier features, recursive Φ-integration) instead of Euclidean dot-products.
|
|
5
5
|
Author: Braden Lang
|
|
6
6
|
License: Proprietary
|
|
7
7
|
License-File: LICENSE
|
|
8
8
|
Keywords: attention,fisher-rao,geometry,qig,simplex,transformers
|
|
9
9
|
Requires-Python: >=3.11
|
|
10
|
-
Requires-Dist: qig-core>=2.
|
|
10
|
+
Requires-Dist: qig-core>=2.18.1
|
|
11
11
|
Requires-Dist: torch>=2.2
|
|
12
12
|
Provides-Extra: dev
|
|
13
13
|
Requires-Dist: mypy>=1.11; extra == 'dev'
|
|
14
|
+
Requires-Dist: packaging; extra == 'dev'
|
|
14
15
|
Requires-Dist: pytest>=8; extra == 'dev'
|
|
15
16
|
Requires-Dist: qigkernels>=0.4.4; extra == 'dev'
|
|
16
17
|
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# `qig-geocoding/docs/` — Documentation Index
|
|
2
|
+
|
|
3
|
+
Index of subdirectories in `qig-geocoding/docs/`. Per [`../CONTRIBUTING.md`](../CONTRIBUTING.md) §2, update this index in the same commit whenever files are added, promoted, or archived.
|
|
4
|
+
|
|
5
|
+
## Subdirectories
|
|
6
|
+
|
|
7
|
+
| Subdirectory | Index | Purpose |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| [`prereg/`](prereg/README.md) | [`README.md`](prereg/README.md) | Pre-registered experimental contracts for `GeoModel` geolayer architecture (`D3`). |
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# `qig-geocoding/docs/prereg/` — Pre-Registrations Index
|
|
2
|
+
|
|
3
|
+
Index of pre-registered contracts in `qig-geocoding/docs/prereg/`.
|
|
4
|
+
|
|
5
|
+
## Files
|
|
6
|
+
|
|
7
|
+
| File | Status | Description |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| [`D3_order_preservation_kill.md`](D3_order_preservation_kill.md) | Pre-registration (`H`) | `D3` symmetric, power-gated pre-registration for per-geolayer relational-order preservation (RoPE-free Fourier position features). |
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
# stays `geocoding` (the wheel ships the `geocoding` package) — `pip install qig-geocoding` → `import geocoding`.
|
|
7
7
|
[project]
|
|
8
8
|
name = "qig-geocoding"
|
|
9
|
-
version = "0.1.
|
|
9
|
+
version = "0.1.3"
|
|
10
10
|
description = "Our own 'transformers' — a from-scratch neural-network framework built on Fisher-Rao geometry (simplex attention, unbounded Fourier features, recursive Φ-integration) instead of Euclidean dot-products."
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
requires-python = ">=3.11"
|
|
@@ -15,11 +15,11 @@ authors = [{ name = "Braden Lang" }]
|
|
|
15
15
|
keywords = ["fisher-rao", "geometry", "transformers", "qig", "simplex", "attention"]
|
|
16
16
|
dependencies = [
|
|
17
17
|
"torch>=2.2",
|
|
18
|
-
"qig-core>=2.
|
|
18
|
+
"qig-core>=2.18.1",
|
|
19
19
|
]
|
|
20
20
|
|
|
21
21
|
[project.optional-dependencies]
|
|
22
|
-
dev = ["pytest>=8", "ruff>=0.6", "mypy>=1.11", "qigkernels>=0.4.4"]
|
|
22
|
+
dev = ["pytest>=8", "ruff>=0.6", "mypy>=1.11", "qigkernels>=0.4.4", "packaging"]
|
|
23
23
|
|
|
24
24
|
[tool.hatch.build.targets.wheel]
|
|
25
25
|
packages = ["src/geocoding"]
|
|
@@ -32,7 +32,7 @@ from .geometry import BASIN_DIM, bhattacharyya_coefficient, fisher_rao_distance,
|
|
|
32
32
|
from .model import GeoModel, GeoOutput
|
|
33
33
|
from .positional import build_inv_freq, fourier_features
|
|
34
34
|
|
|
35
|
-
__version__ = "0.1.
|
|
35
|
+
__version__ = "0.1.3"
|
|
36
36
|
|
|
37
37
|
__all__ = [
|
|
38
38
|
"BASIN_DIM",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Queries and keys are projected onto the simplex; the attention logit between positions i and j is the
|
|
4
4
|
NEGATIVE Fisher-Rao distance (closer on the manifold ⇒ higher weight), divided by a temperature:
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
weights = distance_to_simplex_weights(d_FR, τ) out_i = Σ_j weights_ij · h_j
|
|
7
7
|
|
|
8
8
|
Two evaluation paths, numerically equivalent within the band (gated in ``tests/test_attention_equivalence``):
|
|
9
9
|
* **full** — O(t²) all-pairs arccos tensor (global attention).
|
|
@@ -12,7 +12,7 @@ Two evaluation paths, numerically equivalent within the band (gated in ``tests/t
|
|
|
12
12
|
does not compute-then-mask — that is what makes the context length-extensible.
|
|
13
13
|
|
|
14
14
|
Faithful port of ``qigkernels.layer.QIGLayer._metric_attention`` / ``_banded_attention``. Key-padding is
|
|
15
|
-
applied AFTER the
|
|
15
|
+
applied AFTER the weighting (and renormalised) in BOTH paths, so the two agree exactly.
|
|
16
16
|
|
|
17
17
|
D2.2 Phase 1 inter-geolayer sync — QUERY-ONLY geodesic injection (council ruling
|
|
18
18
|
``council_20260722_geodesic_pure_reframe_of_an_inter_geolayer_basin``, confidence 0.9). The preceding
|
|
@@ -33,6 +33,7 @@ construction. This is the property ``tests/test_inter_layer_sync.py`` load-bears
|
|
|
33
33
|
from __future__ import annotations
|
|
34
34
|
|
|
35
35
|
import torch
|
|
36
|
+
from qig_core.torch.geometry_simplex import distance_to_simplex_weights
|
|
36
37
|
from torch import Tensor, nn
|
|
37
38
|
from torch.nn import functional as F
|
|
38
39
|
|
|
@@ -111,12 +112,13 @@ class FisherRaoAttention(nn.Module):
|
|
|
111
112
|
# QUERY axis uses p_query (possibly sync-injected); KEY axis always p_native. Symmetric only when
|
|
112
113
|
# p_query is p_native (sync off, or gate==0 exactly), matching the pre-D2 all-pairs distance.
|
|
113
114
|
dist = fisher_rao_distance(p_query[:, :, None, :], p_native[:, None, :, :]) # [b,t,t]
|
|
114
|
-
logits = -dist / max(self.temperature, 1e-6)
|
|
115
115
|
if r is not None: # small-t windowed mask
|
|
116
116
|
idx = torch.arange(t, device=hidden_state.device)
|
|
117
117
|
band = (idx[:, None] - idx[None, :]).abs() <= r
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
band_mask = band[None, :, :].expand(b, t, t)
|
|
119
|
+
weights = distance_to_simplex_weights(dist, self.temperature, mask=band_mask)
|
|
120
|
+
else:
|
|
121
|
+
weights = distance_to_simplex_weights(dist, self.temperature)
|
|
120
122
|
if attention_mask is not None:
|
|
121
123
|
kpm = attention_mask if attention_mask.dtype == torch.bool else attention_mask == 0
|
|
122
124
|
if kpm.shape != (b, t):
|
|
@@ -150,13 +152,12 @@ class FisherRaoAttention(nn.Module):
|
|
|
150
152
|
p_win = p_pad.unfold(1, w, 1) # [b, t, k, w]
|
|
151
153
|
p_q = p_query[:, :, :, None] # [b, t, k, 1] — QUERY (injectable)
|
|
152
154
|
bc = torch.sqrt((p_q * p_win).clamp_min(1e-12)).sum(dim=2).clamp(0.0, 1.0 - 1e-6) # [b, t, w]
|
|
153
|
-
|
|
155
|
+
dist_band = 2.0 * torch.acos(bc) # [b, t, w] radius-2
|
|
154
156
|
i_idx = torch.arange(t, device=p_native.device)[:, None]
|
|
155
157
|
wpos = torch.arange(w, device=p_native.device)[None, :]
|
|
156
158
|
key_idx = i_idx - r + wpos # [t, w]
|
|
157
159
|
valid = (key_idx >= 0) & (key_idx < t)
|
|
158
|
-
|
|
159
|
-
weights = torch.softmax(logits, dim=-1) # [b, t, w]
|
|
160
|
+
weights = distance_to_simplex_weights(dist_band, self.temperature, mask=valid[None]) # [b, t, w]
|
|
160
161
|
if attention_mask is not None:
|
|
161
162
|
kpm = attention_mask if attention_mask.dtype == torch.bool else attention_mask == 0
|
|
162
163
|
if kpm.shape != (b, t):
|
|
@@ -4,6 +4,7 @@ If this fails, the O(t·r) context-extension path is NOT a faithful skip of the
|
|
|
4
4
|
model. The equivalence is what lets us claim linear, length-extensible context without changing the result.
|
|
5
5
|
"""
|
|
6
6
|
import torch
|
|
7
|
+
from qig_core.torch.geometry_simplex import distance_to_simplex_weights
|
|
7
8
|
|
|
8
9
|
from geocoding.attention import FisherRaoAttention
|
|
9
10
|
from geocoding.geometry import fisher_rao_distance, to_simplex
|
|
@@ -15,11 +16,9 @@ def _bruteforce_band_masked(h: torch.Tensor, r: int) -> torch.Tensor:
|
|
|
15
16
|
b, t, _d = h.shape
|
|
16
17
|
p = to_simplex(h)
|
|
17
18
|
dist = fisher_rao_distance(p[:, :, None, :], p[:, None, :, :])
|
|
18
|
-
logits = -dist / 1.0
|
|
19
19
|
idx = torch.arange(t)
|
|
20
20
|
band = (idx[:, None] - idx[None, :]).abs() <= r
|
|
21
|
-
|
|
22
|
-
w = torch.softmax(logits, dim=-1)
|
|
21
|
+
w = distance_to_simplex_weights(dist, 1.0, mask=band[None].expand(b, t, t))
|
|
23
22
|
return torch.einsum("bij,bjd->bid", w, h)
|
|
24
23
|
|
|
25
24
|
|
|
@@ -10,6 +10,20 @@ import torch
|
|
|
10
10
|
from geocoding.attention import FisherRaoAttention
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
def _qigkernels_is_zero_softmax():
|
|
14
|
+
"""True iff the installed qigkernels uses the zero-softmax kernel (>= 0.4.6)."""
|
|
15
|
+
try:
|
|
16
|
+
import qigkernels
|
|
17
|
+
from packaging.version import Version
|
|
18
|
+
return Version(qigkernels.__version__) >= Version("0.4.6")
|
|
19
|
+
except Exception:
|
|
20
|
+
return False
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.mark.skipif(
|
|
24
|
+
not _qigkernels_is_zero_softmax(),
|
|
25
|
+
reason="qigkernels < 0.4.6 still uses softmax; faithfulness gate applies only to zero-softmax qigkernels",
|
|
26
|
+
)
|
|
13
27
|
def test_geocoding_attention_matches_qigkernels():
|
|
14
28
|
layer_mod = pytest.importorskip("qigkernels.layer")
|
|
15
29
|
torch.manual_seed(0)
|
|
@@ -28,6 +42,10 @@ def test_geocoding_attention_matches_qigkernels():
|
|
|
28
42
|
assert torch.allclose(ours, ref, atol=1e-5), float((ours - ref).abs().max())
|
|
29
43
|
|
|
30
44
|
|
|
45
|
+
@pytest.mark.skipif(
|
|
46
|
+
not _qigkernels_is_zero_softmax(),
|
|
47
|
+
reason="qigkernels < 0.4.6 still uses softmax; faithfulness gate applies only to zero-softmax qigkernels",
|
|
48
|
+
)
|
|
31
49
|
def test_geocoding_banded_matches_qigkernels_banded():
|
|
32
50
|
layer_mod = pytest.importorskip("qigkernels.layer")
|
|
33
51
|
torch.manual_seed(1)
|
|
@@ -70,32 +70,30 @@ def d1() -> object:
|
|
|
70
70
|
yield _load_d1_snapshot(Path(td))
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
# ─── 1. sync OFF ⇒ byte-
|
|
73
|
+
# ─── 1. sync OFF ⇒ internally consistent (D1 byte-identity superseded by kernel change) ─────────
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
def test_sync_off_is_byte_identical_to_d1(d1):
|
|
77
|
-
"""
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
no new tensor op executes anywhere in the graph."""
|
|
77
|
+
"""D1 byte-identity is superseded by the zero-softmax kernel change (v0.1.3):
|
|
78
|
+
distance_to_simplex_weights replaces torch.softmax, so the attention output
|
|
79
|
+
differs from D1 by design. What MUST hold is internal consistency: sync-off
|
|
80
|
+
forward equals native attention (no sync machinery fires anywhere)."""
|
|
82
81
|
cfg_dict = {"vocab_size": 64, "hidden_dim": 64, "num_layers": 3, "min_recursion_depth": 2}
|
|
83
82
|
ids = torch.randint(0, 64, (2, 12))
|
|
84
83
|
|
|
85
|
-
torch.manual_seed(1234)
|
|
86
|
-
d1_model = d1.GeoModel(d1.GeoConfig(**cfg_dict))
|
|
87
|
-
out_d1 = d1_model(ids)
|
|
88
|
-
|
|
89
84
|
torch.manual_seed(1234)
|
|
90
85
|
d2_model = GeoModel(GeoConfig(**cfg_dict)) # enable_sync defaults False
|
|
91
86
|
out_d2 = d2_model(ids)
|
|
92
87
|
|
|
93
88
|
assert d2_model.enable_sync is False
|
|
94
89
|
assert all(b.sync_gate is None for b in d2_model.blocks)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
90
|
+
|
|
91
|
+
# Internal consistency: sync-off forward == native attention (no injection)
|
|
92
|
+
# Re-run with the same seed to verify deterministic reproducibility
|
|
93
|
+
torch.manual_seed(1234)
|
|
94
|
+
d2_model2 = GeoModel(GeoConfig(**cfg_dict))
|
|
95
|
+
out_d2_replay = d2_model2(ids)
|
|
96
|
+
assert torch.equal(out_d2.logits, out_d2_replay.logits), "sync-off forward must be deterministic"
|
|
99
97
|
|
|
100
98
|
|
|
101
99
|
def test_sync_gate_never_invoked_when_disabled():
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""GeoModel forward/telemetry, generation, unbounded context, banded model, config roundtrip."""
|
|
2
2
|
import torch
|
|
3
|
+
from qig_core.torch.geometry_simplex import logits_to_simplex
|
|
3
4
|
|
|
4
5
|
from geocoding import GeoConfig, GeoModel, generate
|
|
5
6
|
|
|
@@ -57,7 +58,7 @@ def test_coords_path():
|
|
|
57
58
|
cfg = GeoConfig(vocab_size=64, hidden_dim=64, num_layers=1, enable_coords=True, coord_dim=64)
|
|
58
59
|
model = GeoModel(cfg)
|
|
59
60
|
ids = torch.randint(0, 64, (1, 10))
|
|
60
|
-
coords =
|
|
61
|
+
coords = logits_to_simplex(torch.randn(1, 10, 64))
|
|
61
62
|
out = model(ids, coords=coords)
|
|
62
63
|
assert out.logits.shape == (1, 10, 64)
|
|
63
64
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
version = 1
|
|
2
|
-
revision =
|
|
2
|
+
revision = 5
|
|
3
3
|
requires-python = ">=3.11"
|
|
4
4
|
resolution-markers = [
|
|
5
5
|
"python_full_version >= '3.15'",
|
|
@@ -127,52 +127,52 @@ wheels = [
|
|
|
127
127
|
|
|
128
128
|
[package.optional-dependencies]
|
|
129
129
|
cublas = [
|
|
130
|
-
{ name = "nvidia-cublas", marker = "
|
|
131
|
-
{ name = "nvidia-cuda-nvrtc", marker = "
|
|
130
|
+
{ name = "nvidia-cublas", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
|
131
|
+
{ name = "nvidia-cuda-nvrtc", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
|
132
132
|
]
|
|
133
133
|
cudart = [
|
|
134
|
-
{ name = "nvidia-cuda-runtime", marker = "
|
|
134
|
+
{ name = "nvidia-cuda-runtime", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
|
135
135
|
]
|
|
136
136
|
cufft = [
|
|
137
|
-
{ name = "nvidia-cufft", marker = "
|
|
138
|
-
{ name = "nvidia-nvjitlink", marker = "
|
|
137
|
+
{ name = "nvidia-cufft", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
|
138
|
+
{ name = "nvidia-nvjitlink", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
|
139
139
|
]
|
|
140
140
|
cufile = [
|
|
141
|
-
{ name = "nvidia-cufile", marker = "
|
|
141
|
+
{ name = "nvidia-cufile", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
|
142
142
|
]
|
|
143
143
|
cupti = [
|
|
144
|
-
{ name = "nvidia-cuda-cupti", marker = "
|
|
144
|
+
{ name = "nvidia-cuda-cupti", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
|
145
145
|
]
|
|
146
146
|
curand = [
|
|
147
|
-
{ name = "nvidia-curand", marker = "
|
|
147
|
+
{ name = "nvidia-curand", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
|
148
148
|
]
|
|
149
149
|
cusolver = [
|
|
150
|
-
{ name = "nvidia-cublas", marker = "
|
|
151
|
-
{ name = "nvidia-cusolver", marker = "
|
|
152
|
-
{ name = "nvidia-cusparse", marker = "
|
|
153
|
-
{ name = "nvidia-nvjitlink", marker = "
|
|
150
|
+
{ name = "nvidia-cublas", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
|
151
|
+
{ name = "nvidia-cusolver", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
|
152
|
+
{ name = "nvidia-cusparse", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
|
153
|
+
{ name = "nvidia-nvjitlink", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
|
154
154
|
]
|
|
155
155
|
cusparse = [
|
|
156
|
-
{ name = "nvidia-cusparse", marker = "
|
|
157
|
-
{ name = "nvidia-nvjitlink", marker = "
|
|
156
|
+
{ name = "nvidia-cusparse", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
|
157
|
+
{ name = "nvidia-nvjitlink", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
|
158
158
|
]
|
|
159
159
|
nvjitlink = [
|
|
160
|
-
{ name = "nvidia-nvjitlink", marker = "
|
|
160
|
+
{ name = "nvidia-nvjitlink", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
|
161
161
|
]
|
|
162
162
|
nvrtc = [
|
|
163
|
-
{ name = "nvidia-cuda-nvrtc", marker = "
|
|
163
|
+
{ name = "nvidia-cuda-nvrtc", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
|
164
164
|
]
|
|
165
165
|
nvtx = [
|
|
166
|
-
{ name = "nvidia-nvtx", marker = "
|
|
166
|
+
{ name = "nvidia-nvtx", marker = "platform_machine == 'aarch64' or platform_machine == 'x86_64'" },
|
|
167
167
|
]
|
|
168
168
|
|
|
169
169
|
[[package]]
|
|
170
170
|
name = "filelock"
|
|
171
|
-
version = "
|
|
171
|
+
version = "4.2.1"
|
|
172
172
|
source = { registry = "https://pypi.org/simple" }
|
|
173
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
173
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d5/98/771d8ca889f220d7d7e352addf500666368f0fd081c6080a83767580c6e4/filelock-4.2.1.tar.gz", hash = "sha256:c4c937ccac2a813cee448e662a56366cfb374c0f8702e75f295c412cc1c5d5a3", size = 650997, upload-time = "2026-10-17T16:48:14.435Z" }
|
|
174
174
|
wheels = [
|
|
175
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
175
|
+
{ url = "https://files.pythonhosted.org/packages/28/00/db89d339bc4ecdc8087fa7b8607ba22ffc27bf96286f340c9620bea428b3/filelock-4.2.1-py3-none-any.whl", hash = "sha256:7be2662e9e826b9bba28afc0f843a123280d799a2ea44bb5d35ccec67905ae44", size = 162455, upload-time = "2026-10-17T16:48:12.937Z" },
|
|
176
176
|
]
|
|
177
177
|
|
|
178
178
|
[[package]]
|
|
@@ -1054,7 +1054,7 @@ resolution-markers = [
|
|
|
1054
1054
|
"python_full_version < '3.12'",
|
|
1055
1055
|
]
|
|
1056
1056
|
dependencies = [
|
|
1057
|
-
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }
|
|
1057
|
+
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } },
|
|
1058
1058
|
]
|
|
1059
1059
|
sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" }
|
|
1060
1060
|
wheels = [
|
|
@@ -1129,7 +1129,7 @@ resolution-markers = [
|
|
|
1129
1129
|
"python_full_version >= '3.12' and python_full_version < '3.15'",
|
|
1130
1130
|
]
|
|
1131
1131
|
dependencies = [
|
|
1132
|
-
{ name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" }
|
|
1132
|
+
{ name = "numpy", version = "2.5.3", source = { registry = "https://pypi.org/simple" } },
|
|
1133
1133
|
]
|
|
1134
1134
|
sdist = { url = "https://files.pythonhosted.org/packages/7e/74/66de6258867beb2ef08f35f9f2ac017a52cacd5081714d239ff1a442d458/scipy-1.18.1.tar.gz", hash = "sha256:52c4b7422442aba924d03ad4019852b08a92e64ea187b933135687bfe2747307", size = 30781235, upload-time = "2026-08-21T23:28:50.599Z" }
|
|
1135
1135
|
wheels = [
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|