File size: 11,935 Bytes
9e551a1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 | # Tensor Roll β process / IR boundaries
Copyright (C) 2026 SnapKitty Collective β AGPLv3.
This document states exactly where one execution domain ends and another
begins. There are three boundaries that matter; none of them is a wrapper
around an existing framework, and none of them is invented.
## 1. Classical dispatch boundary: `TensorRoll` β GEMM vs quantum candidate
Location: `tensor_roll/core.py` β `dispatch_matmul()` and
`evaluate_options()` / `qsim_encode_for_roll`.
The recursive operator `TensorRoll(T, axis, depth, rank, quantum_policy)`
partitions a tensor and evaluates candidate representations per leaf.
Ordinary candidates (PRESERVE / QUANTIZE / FACTORIZE / MERGED / ROUTED /
RECONSTRUCTED / PRUNED) execute as NumPy on CPU and every report labels
them `backend: cpu-numpy`.
A leaf is *eligible* for the quantum path only when all of these hold:
- `quantum_policy != "off"` (CLI: `roll --quantum-policy`,
default for `demo` is `explore`),
- the leaf block has at most `QUANTUM_ELIGIBLE_MAX = 256` elements
(8 qubits β an honest near-term register size),
- a qsim encoder is supplied.
Eligible leaves gain QUANTUM-ENCODED / RECONSTRUCTED / ROUTED candidates
whose numbers come from the real statevector simulator in
`tensor_roll/qsim.py`. Their reports are labeled
`backend: qsim-classical`, `device: cpu (statevector simulator)`.
That label is the boundary: it is classical linear algebra (complex128
statevectors, explicit unitary application, Born-rule sampling), never a
QPU, and it never claims to be one.
On a machine with the NVIDIA CUDA-Q toolchain, the same kernel family in
`cudaq/tensor_roll_kernels.py` can execute for real
(`run_smoke()` targets `qpp-cpu`); on this machine the import fails and the
module prints `unavailable` instead of pretending.
## 2. CUDA-Q β Q# boundary: serialized trace artifact (real, not an API)
There is no direct CUDA-Q β Q# linkage β the two toolchains do not share
an in-process API, and inventing one would be fabrication. The real
boundary is a serialized artifact produced by `tensor_roll/boundary.py`:
- `trace_for_kernel(kernel, n_qubits, angles, depth)` builds the gate-level
IR: the exact gate sequence (`ry` / `cnot` / `measure` / `reset`) that
both `cudaq/tensor_roll_kernels.py` and `qsharp/TensorRoll.qs` implement.
Each gate carries its QIR op name (`__quantum__qis__ry__body`, β¦) so the
trace maps 1:1 onto QIR when both vendor toolchains are present.
- `write_trace(path, ...)` writes `{sha256, trace}` JSON: schema version,
kernel name, qubit count, shots, the gate list, the measurement counts,
producer label, and timestamp.
- `read_trace(path)` refuses the file if the SHA-256 does not match or the
schema is wrong.
- `counts_to_amplitudes(trace)` is the shared classical reconstruction
estimator (amplitude = sqrt(count/shots)) β classical by construction on
every side of the boundary.
Data flow:
```
CUDA-Q process (cudaq/tensor_roll_kernels.py)
β executes kernels (needs CUDA-Q toolchain + target)
βΌ
boundary.json {gates, counts, sha256} <-- the boundary: a file
β²
Q# toolchain (qsharp/TensorRoll.qs)
β implements the same gate sequence; reads/writes the artifact
```
`tensor-roll qkernel` writes `qkernel/boundary.json` on every run and
immediately re-reads it to prove the round trip. Corrupt the file and the
reader rejects it β verified by the test suite.
What is NOT claimed: at no point does tensor-roll convert CUDA-Q objects
to Q# objects in memory. The handoff is serialization, honestly labeled.
## 3. Sandbox boundary: supervisor β untrusted child
Location: `tensor_roll/sandbox.py`, exercised by `tensor-roll sandbox`.
- The child runs in its own session/process group (`start_new_session`),
with rlimits (address space, CPU, file size, process count), a filtered
environment (only `PATH`/`LANG`/`TZ`/`HOME` survive), and `cwd` pinned to
a jail directory.
- Policy: absolute paths must resolve inside the jail; the binary must be
on an allowlist or not on the denylist (deny wins). Network namespaces
are entered via `unshare(CLONE_NEWNET)` when the kernel permits it β
probed at startup and reported (`netns_available=True/False`); on this
machine the probe passes, so `qkernel`-adjacent untrusted code genuinely
loses network access.
- Every spawn, policy verdict, timeout, and signal is appended as JSONL to
`sandbox/audit.log`.
Signals: on timeout the supervisor kills the whole process group
(`killpg(SIGKILL)`), then reaps strays the same way. The audit log records
which.
## 4. What runs where (summary table)
| Component | Executes on | Label in reports |
|---|---|---|
| Tensor math, training, search | NumPy / CPU | `cpu-numpy` |
| Quantum kernel candidates | classical statevector sim | `qsim-classical` |
| CUDA-Q kernels | NVIDIA toolchain (absent here) | `unavailable` here |
| Q# operations | Q# toolchain (absent here) | `unavailable` here |
| CUDA-Q β Q# handoff | serialized `boundary.json` | sha256-verified |
| Untrusted subprocesses | sandboxed child | audit-logged |
If a row says `unavailable`, no number was invented for it β the CLI
prints `unavailable` and the docs say what hardware would change that
(see `docs/ARCHITECTURE.md`).
## Integration state (failure-resolution audit, 2026-09-29)
**Finding: file-mediated.** The CUDA-Q β Q# handoff in this repository is a
checksummed serialized JSON trace artifact on disk β not a direct in-process
API, not live QIR/MLIR bitcode exchange, and not process-to-process RPC.
Vendor-toolchain execution on this machine is additionally **source-only**:
CUDA, CUDA-Q, and Q# all probe UNAVAILABLE here, so no vendor kernel has
ever executed on this box. The file artifact itself round-trips for real
(`tensor-roll qkernel` writes `qkernel/boundary.json` and re-reads it;
`test_trace_roundtrip` corrupts it and watches the reader refuse).
Candidates considered and rejected:
- **direct** β rejected. `tensor_roll/boundary.py` states the two toolchains
"do not share an in-process API", and no code path converts CUDA-Q
objects to Q# objects in memory.
- **IR-mediated** (true QIR/MLIR bitcode) β rejected for this machine.
`boundary.py` notes "True QIR/MLIR bitcode exchange requires both vendor
toolchains installed"; neither is installed, so the QIR op names carried
on each gate (`__quantum__qis__ry__body`, β¦) are a mapping specification,
not an executed exchange.
- **process-mediated** β rejected. No live RPC exists between a CUDA-Q
process and a Q# process; the handoff is a file both sides can write/read.
- **file-mediated** β ACCEPTED. Evidence below.
- **source-only** β true of *execution* on this machine (see probes), but
the integration mechanism itself is richer than source-only: the trace
artifact is really written, checksummed, read, and verified on every
`qkernel` run via the `qsim-classical` path.
File/line evidence (from `tensor_roll/doctor.py::integration_state()`):
- `tensor_roll/boundary.py:7` β "an in-process API. The real boundary used
by tensor-roll is a serialized," [no direct linkage; artifact, not API]
- `tensor_roll/boundary.py:8` β "checksummed, versioned artifact
containing:" [integrity-checked artifact]
- `tensor_roll/boundary.py:20` β "True QIR/MLIR bitcode exchange requires
both vendor toolchains installed;" [true QIR/MLIR exchange needs
toolchains]
- `tensor_roll/boundary.py:57` β `def trace_for_kernel(...)` [gate IR
constructor: the single source of truth both kernel families implement]
- `tensor_roll/boundary.py:102` β `def write_trace(...)` [trace is WRITTEN
to disk as `{sha256, trace}` JSON]
- `tensor_roll/boundary.py:130` β `def read_trace(...)` [trace is READ from
disk; checksum/schema mismatch raises]
- `tensor_roll/cli.py:291` β `bpath = os.path.join(wdir, "qkernel",
"boundary.json")` [CLI writes the file artifact]
- `tensor_roll/cli.py:296` β `back = BD.read_trace(bpath)` [CLI verifies
the round trip immediately]
- `cudaq/tensor_roll_kernels.py:9` β "These kernels are NOT executed by the
tensor-roll CLI on machines without" [the CUDA-Q toolchain]
- `cudaq/tensor_roll_kernels.py:28` β `_HAS_CUDAQ = False`
[toolchain-absent fallback path]
- `qsharp/TensorRoll.qs:13` β "NOTE: not compiled or executed in this
repository's test environment" [source-only on this machine]
Live probes on this machine (`tensor-roll doctor`, 2026-09-29):
- CUDA: UNAVAILABLE β no nvidia-smi on PATH, no `/dev/nvidia*` devices.
- CUDA-Q: UNAVAILABLE β no toolchain (`ModuleNotFoundError` from a neutral
directory; the repo-local `cudaq/` directory is a namespace package of
kernel *source*, not the NVIDIA toolchain).
- Q#: UNAVAILABLE β no toolchain and no `dotnet`; the repo-local `qsharp/`
directory is operation *source*, not an installed toolchain.
- GPU: NONE. CPU (NumPy) executes for real and passes.
What would change this determination: installing the NVIDIA CUDA-Q
toolchain and a Q# toolchain would move the *execution* rows from
source-only to executed, but the integration state would remain
file-mediated until a real QIR/MLIR bitcode handoff (not just a JSON trace
carrying QIR op names) is demonstrated between the two toolchains.
## Failure-resolution modules (2026-09-29)
### Out-of-core boundary (`tensor_roll/ooc.py`)
The streaming path never confuses "addressable" with "in RAM":
- A `VirtualManifest` carries shapes, dtypes, and byte offsets only.
`reader()` raises `RuntimeError` β there is no silent path from a
virtual manifest to weight data.
- `MappedTensor.view` is an `np.memmap` view; `TensorWindow.data`
materializes only the window. `RollWorkspace` accounts the logical
working set against an explicit ceiling and raises
`MemoryBudgetExceeded` past it. Measured: virtual 30B planning peaks
at 35MB RSS β the bound is demonstrated, not asserted.
- `StudentWriter` checksums every emitted tensor (SHA-256); `verify()`
re-hashes on resume and refuses mismatches. Checkpoints are JSON +
SHA-256 with atomic replace; corruption is refused, never loaded.
- The `.npz` repack path reads one array at a time into the shard
format; `.safetensors` is windowed in place via mmap with zero copy.
### Planner boundary (`tensor_roll/planner.py`)
- `plan()` lands the allocation on the requested target (β€ 8.0B by
default) and never above the 8.5B hard cap. Sensitivity is measured
from sampled windows (`calibrate_sensitivity`) or documented as
uniform 0.5 when no data exists β the planner degrades to a uniform
ratio rather than inventing sensitivity.
- The 30B STRUCTURAL SCALE TEST label is load-bearing: it certifies
storage addressing and planning under bounded RAM, and explicitly
does **not** certify model quality. Actual 30B weights: NOT TESTED.
### Fidelity boundary (`tensor_roll/fidelity.py`)
- `divergence_trace()` measures on teacher-forced contexts so model
divergence is isolated from context drift; free-run agreement
(the 3/24 number) is reported separately and never mixed with it.
- `fidelity_aware_select()` applies the divergence penalty uniformly to
every decision class from measured (sensitivity, reconstruction
error). Quantum-gated options are scored from their own qsim-measured
errors β the penalty cannot prefer or punish a backend beyond what
the measurements say.
- Ablations replace student components with shape-aligned teacher
weights and report Ξagreement/ΞKL; components that cannot be aligned
are skipped and reported as skipped, not zeroed.
### INT4 boundary (`tensor_roll/quant.py`)
- Scheme choice (per-tensor / per-channel / group-wise) is the argmin
of measured MSE per tensor, never a hard-coded default.
- `recovery_train()` reports PPL before training, after training, and
after requantization; "improved" is a measured boolean, not a claim.
|