Uploaded using `kernel-builder`.

#1
by phanerozoic - opened
build/torch213-cxx11-cu130-aarch64-linux/__init__.py ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """exact-solve: exact rational linear-system solver on INT8 tensor cores.
2
+
3
+ Solves ``A x = b`` exactly over the rationals for integer ``A`` (``[n, n]``) and
4
+ integer ``b`` (``[n, c]``) by high-order Dixon p-adic lifting on the residue
5
+ (CRT) engine, and returns the exact solution as ``fractions.Fraction``.
6
+
7
+ The lift uses a squarefree modulus ``P = prod(q_j)`` over primes ``q_j < 256``:
8
+ each step's ``A^{-1} mod q_j`` matmuls are native INT8 tensor-core GEMMs (one per
9
+ prime), CRT-reconstructed into a wide base-``P`` digit; the single-limb residual
10
+ update is ``(r - A x_i) * P^{-1} mod 2^64`` (``P`` odd), which needs only
11
+ ``A x_i mod 2^64``. The batched modular inverse and the lift run on the GPU; the
12
+ final rational reconstruction runs on the CPU (accelerated by ``gmpy2`` and a
13
+ process pool, ``fork`` or ``spawn``, when available).
14
+
15
+ solve(A, b) -> [n][c] list of fractions.Fraction (exact)
16
+
17
+ Reference: Dixon, "Exact solution of linear equations using p-adic expansions"
18
+ (Numerische Mathematik 40, 1982).
19
+ """
20
+ import math
21
+ import os
22
+ import sys
23
+ from fractions import Fraction
24
+
25
+ import torch
26
+
27
+ from ._ops import ops
28
+
29
+ # odd primes < 256 (descending); 2 is excluded so P is odd and invertible mod 2^64
30
+ _ODD_PRIMES = [251, 241, 239, 233, 229, 227, 223, 211, 199, 197, 193, 191, 181,
31
+ 179, 173, 167, 163, 157, 151, 149, 139, 137, 131, 127, 113, 109,
32
+ 107, 103, 101, 97, 89, 83, 79, 73, 71, 67, 61, 59, 53, 47, 43, 41,
33
+ 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3]
34
+ _LMAX = 4
35
+ _STRIDE = 2 * _LMAX + 1
36
+ _M64 = (1 << 64) - 1
37
+
38
+
39
+ # ---------------------------------------------------------------- tables / ops
40
+
41
+ def _crt_tables(primes, device):
42
+ P = len(primes)
43
+ inv = [0] * (P * P)
44
+ for a in range(P):
45
+ for b in range(a + 1, P):
46
+ inv[a * P + b] = pow(primes[a] % primes[b], -1, primes[b])
47
+ mu = [(1 << 62) // p for p in primes]
48
+ pow32 = [0] * (P * _STRIDE)
49
+ for pi, p in enumerate(primes):
50
+ for k in range(_STRIDE):
51
+ pow32[pi * _STRIDE + k] = pow(2, 32 * k, p)
52
+ return (torch.tensor(primes, dtype=torch.int32, device=device),
53
+ torch.tensor(inv, dtype=torch.int32, device=device),
54
+ torch.tensor(mu, dtype=torch.int64, device=device),
55
+ torch.tensor(pow32, dtype=torch.int32, device=device))
56
+
57
+
58
+ def _pad8(x):
59
+ return max(24, ((x + 7) // 8) * 8)
60
+
61
+
62
+ def _extract(Xi64, rows_pad, cols_pad, primes, mu, pow32):
63
+ rows, cols = Xi64.shape
64
+ if rows == rows_pad and cols == cols_pad:
65
+ Y = Xi64.contiguous()
66
+ else:
67
+ Y = torch.zeros(rows_pad, cols_pad, dtype=torch.int64, device=Xi64.device)
68
+ Y[:rows, :cols] = Xi64
69
+ planes = torch.empty(primes.numel(), rows_pad, cols_pad, dtype=torch.int8, device=Xi64.device)
70
+ ops.exact_solve_extract(planes, Y.unsqueeze(0).contiguous(), primes, mu, pow32)
71
+ return planes
72
+
73
+
74
+ def _matmul(A_planes, B_planes, Lc, primes, inv, mu):
75
+ C = torch.empty(Lc, A_planes.size(1), B_planes.size(1), dtype=torch.int64, device=A_planes.device)
76
+ ops.exact_solve_matmul(C, A_planes, B_planes, primes, inv, mu)
77
+ return C
78
+
79
+
80
+ def _batched_modinv(A, primes):
81
+ n = A.size(0)
82
+ E = primes.numel()
83
+ Bout = torch.empty(E, n, n, dtype=torch.int8, device=A.device)
84
+ work = torch.empty((int)(E) * n * 2 * n, dtype=torch.int32, device=A.device)
85
+ singular = torch.zeros(E, dtype=torch.int32, device=A.device)
86
+ ops.exact_solve_batched_modinv(Bout, A, primes, work, singular)
87
+ return Bout, singular
88
+
89
+
90
+ def _i64_matmul(A, X):
91
+ out = torch.empty(A.size(0), X.size(1), dtype=torch.int64, device=A.device)
92
+ ops.exact_solve_i64_matmul(out, A, X)
93
+ return out
94
+
95
+
96
+ def _padic(r, axlow, Pinv64):
97
+ out = torch.empty_like(r)
98
+ ops.exact_solve_padic_residual(out, r, axlow, Pinv64)
99
+ return out
100
+
101
+
102
+ def _gpu_recon(digits, Pl, Pm, Rr, Pmhalf, dl, LO):
103
+ out = torch.empty(LO, digits.size(2), dtype=torch.int64, device=digits.device)
104
+ ops.exact_solve_gpu_recon(out, digits, Pl, Pm, Rr, Pmhalf, dl)
105
+ return out
106
+
107
+
108
+ def _s64(u):
109
+ return u - (1 << 64) if u >> 63 else u
110
+
111
+
112
+ def _to_limbs(x, L):
113
+ return torch.tensor([_s64((x >> (64 * i)) & _M64) for i in range(L)], dtype=torch.int64)
114
+
115
+
116
+ def _from_limbs(col, LO):
117
+ u = 0
118
+ for i in range(LO):
119
+ u |= (int(col[i]) & _M64) << (64 * i)
120
+ return u - (1 << (64 * LO)) if u >> (64 * LO - 1) else u
121
+
122
+
123
+ # ---------------------------------------------------------------- reconstruction
124
+ # Reconstruction lives in the torch-free _exact_solve_recon module (imported
125
+ # top-level, its dir on sys.path) so spawn-pool workers load it without torch.
126
+
127
+ _RECON = None
128
+
129
+
130
+ def _recon():
131
+ global _RECON
132
+ if _RECON is None:
133
+ import importlib
134
+ d = os.path.dirname(os.path.abspath(__file__))
135
+ if d not in sys.path:
136
+ sys.path.append(d)
137
+ _RECON = importlib.import_module("_exact_solve_recon")
138
+ return _RECON
139
+
140
+
141
+ _POOL = None
142
+
143
+
144
+ def _pool():
145
+ global _POOL
146
+ if _POOL is None:
147
+ import multiprocessing as mp
148
+ from concurrent.futures import ProcessPoolExecutor
149
+ _recon() # place the backend dir on sys.path before any worker spawns
150
+ ctx = mp.get_context("fork" if hasattr(os, "fork") else "spawn")
151
+ _POOL = ProcessPoolExecutor(max_workers=min(int(os.cpu_count() or 8), 32), mp_context=ctx)
152
+ return _POOL
153
+
154
+
155
+ def _hadamard_bits(A, B):
156
+ n = len(A)
157
+ col_sq = [sum(int(A[i][j]) ** 2 for i in range(n)) for j in range(n)]
158
+ logcol = [0.5 * math.log2(max(s, 1)) for s in col_sq]
159
+ logden = sum(logcol)
160
+ b_sq_max = max(sum(int(B[i][k]) ** 2 for i in range(n)) for k in range(len(B[0])))
161
+ lognum = sum(logcol) - min(logcol) + 0.5 * math.log2(max(b_sq_max, 1))
162
+ return lognum, logden
163
+
164
+
165
+ def _to_int_lists(T, rows, cols):
166
+ if isinstance(T, torch.Tensor):
167
+ t = T.detach().cpu().tolist()
168
+ else:
169
+ t = [list(r) for r in T]
170
+ return [[int(t[i][j]) for j in range(cols)] for i in range(rows)]
171
+
172
+
173
+ # ---------------------------------------------------------------- public solve
174
+
175
+ def solve(A, b, parallel=True):
176
+ """Exact rational solution of ``A x = b``.
177
+
178
+ Args:
179
+ A: ``[n, n]`` integer matrix (torch int tensor, list, or numpy array).
180
+ b: ``[n, c]`` integer right-hand side(s). A 1-D ``[n]`` vector is
181
+ treated as a single column.
182
+ parallel: use a process pool (fork or spawn) for reconstruction when
183
+ available.
184
+
185
+ Returns:
186
+ ``[n][c]`` list of ``fractions.Fraction`` (the exact solution), or
187
+ ``None`` if ``A`` is singular over the rationals.
188
+
189
+ Requires a CUDA device; entries with ``n * max|A_ij| < 2^63``.
190
+ """
191
+ import numpy as np
192
+ dev = A.device if isinstance(A, torch.Tensor) else torch.device("cuda")
193
+ if isinstance(b, torch.Tensor) and b.dim() == 1:
194
+ b = b.unsqueeze(1)
195
+ n = A.shape[0] if isinstance(A, torch.Tensor) else len(A)
196
+ c = (b.shape[1] if isinstance(b, torch.Tensor) else len(b[0]))
197
+ A_list = _to_int_lists(A, n, n)
198
+ b_list = _to_int_lists(b, n, c)
199
+ lognum, logden = _hadamard_bits(A_list, b_list)
200
+ margin = 48
201
+
202
+ A_t = torch.tensor(A_list, dtype=torch.int64, device=dev)
203
+ primes_all = torch.tensor(_ODD_PRIMES, dtype=torch.int32, device=dev)
204
+ Bout_all, singular = _batched_modinv(A_t, primes_all)
205
+ good = [i for i in range(len(_ODD_PRIMES)) if int(singular[i]) == 0]
206
+ if not good:
207
+ return None
208
+ Q = [_ODD_PRIMES[i] for i in good]
209
+ P = 1
210
+ for q in Q:
211
+ P *= q
212
+ Pinv = pow(P, -1, 1 << 64)
213
+ Pinv_s = Pinv - (1 << 64) if Pinv >> 63 else Pinv
214
+ Lx = 1
215
+ while (1 << (64 * Lx - 1)) <= P:
216
+ Lx += 1
217
+ m = int(math.ceil((1.0 + lognum + logden + margin) / math.log2(P)))
218
+ Mmod = P ** m
219
+ Nb = 1 << int(math.ceil(lognum + margin / 2))
220
+ Db = 1 << int(math.ceil(logden + margin / 2))
221
+ primes, inv, mu, pow32 = _crt_tables(Q, dev)
222
+ Ecnt = len(Q)
223
+ Mp, Kp, Cp = max(n, 17), _pad8(n), max(8, ((c + 7) // 8) * 8) # narrow RHS pad for small c
224
+ Blift = Bout_all[torch.tensor(good, device=dev)].contiguous()
225
+ Blift_p = torch.zeros(Ecnt, Mp, Kp, dtype=torch.int8, device=dev)
226
+ Blift_p[:, :n, :n] = Blift
227
+
228
+ r = torch.tensor(b_list, dtype=torch.int64, device=dev)
229
+ digits = torch.empty(m, Lx, n, c, dtype=torch.int64, device=dev)
230
+ for it in range(m):
231
+ rT = r.t().contiguous()
232
+ rT_planes = _extract(rT, Cp, Kp, primes, mu, pow32)
233
+ xi = _matmul(Blift_p, rT_planes, Lx, primes, inv, mu)[:, :n, :c].contiguous()
234
+ digits[it] = xi
235
+ r = _padic(r, _i64_matmul(A_t, xi[0].contiguous()), Pinv_s)
236
+
237
+ NC = n * c
238
+ D3 = np.ascontiguousarray(digits.permute(0, 2, 3, 1).reshape(m, NC, Lx).cpu().numpy())
239
+
240
+ # Reconstruct every entry independently to its exact (num, den). The bounds
241
+ # give Nb*Db <= Mmod/2, so rational reconstruction is unique and correct per
242
+ # entry; no shared common-denominator estimate is involved. Parallelize the
243
+ # per-entry Euclid across cores for large grids (fork or spawn pool).
244
+ rb = _recon()
245
+ use_pool = parallel and NC >= 256
246
+ pairs = None
247
+ if use_pool:
248
+ try:
249
+ nch = min(NC, min(int(os.cpu_count() or 8), 32) * 3)
250
+ bnd = [round(x * NC / nch) for x in range(nch + 1)]
251
+ tasks = [(D3[:, bnd[i]:bnd[i + 1], :].copy(), m, P, Mmod, Nb, Db)
252
+ for i in range(nch) if bnd[i + 1] > bnd[i]]
253
+ pairs = []
254
+ for res in _pool().map(rb._recon_chunk, tasks):
255
+ pairs.extend(res)
256
+ except Exception:
257
+ pairs = None
258
+ if pairs is None:
259
+ pairs = rb._recon_chunk((D3, m, P, Mmod, Nb, Db))
260
+ if any(p is None for p in pairs):
261
+ return None
262
+ return [[Fraction(pairs[i * c + j][0], pairs[i * c + j][1]) for j in range(c)] for i in range(n)]
263
+
264
+
265
+ __all__ = ["solve"]
build/torch213-cxx11-cu130-aarch64-linux/_exact_solve_cuda_31a8fa7_dirty.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ab909fca772b391348b267060579307c2f63977d5cf3d1c8c2e1542d25dcf740
3
+ size 1496576
build/torch213-cxx11-cu130-aarch64-linux/_exact_solve_recon.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Torch-free rational reconstruction (Horner + extended-Euclid) for exact-solve;
2
+ imported by the reconstruction process-pool workers (gmpy2/numpy only, no torch)."""
3
+ import math
4
+
5
+ try:
6
+ import gmpy2
7
+ from gmpy2 import mpz
8
+ _HAVE_GMP = True
9
+ except Exception: # pragma: no cover - optional acceleration
10
+ _HAVE_GMP = False
11
+
12
+ def mpz(x):
13
+ return int(x)
14
+
15
+
16
+ def _rat_recon(X, M, N, D):
17
+ """Rational reconstruction: a/b with a == b X (mod M), |a|<=N, 0<b<=D."""
18
+ r0, r1 = mpz(M), mpz(X) % mpz(M)
19
+ t0, t1 = mpz(0), mpz(1)
20
+ while r1 > N:
21
+ q = r0 // r1
22
+ r0, r1 = r1, r0 - q * r1
23
+ t0, t1 = t1, t0 - q * t1
24
+ a, b = r1, t1
25
+ if b < 0:
26
+ a, b = -a, -b
27
+ if b == 0 or b > D:
28
+ return None
29
+ g = math.gcd(int(abs(a)), int(b)) if not _HAVE_GMP else gmpy2.gcd(abs(a), b)
30
+ a, b = a // g, b // g
31
+ return (int(a), int(b)) if b <= D else None
32
+
33
+
34
+ def _recon_chunk(args):
35
+ """Reconstruct every entry in the chunk: Horner the p-adic digits into X, then
36
+ extended-Euclid X (mod M) into an exact (num, den) pair, each independently."""
37
+ D3chunk, m, P, Mmod, Nb, Db = args
38
+ Pm = mpz(P); Mmz = mpz(Mmod)
39
+ out = []
40
+ for k in range(D3chunk.shape[1]):
41
+ X = mpz(0)
42
+ for it in range(m - 1, -1, -1):
43
+ X = X * Pm + int.from_bytes(D3chunk[it, k].tobytes(), "little", signed=True)
44
+ out.append(_rat_recon(X % Mmz, Mmz, Nb, Db))
45
+ return out
build/torch213-cxx11-cu130-aarch64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _exact_solve_cuda_31a8fa7_dirty
3
+ ops = torch.ops._exact_solve_cuda_31a8fa7_dirty
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_exact_solve_cuda_31a8fa7_dirty::{op_name}"
build/torch213-cxx11-cu130-aarch64-linux/exact_solve/__init__.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ctypes
2
+ import importlib.util
3
+ import sys
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+
7
+
8
+ def _import_from_path(file_path: Path) -> ModuleType:
9
+ # We cannot use the module name as-is, after adding it to `sys.modules`,
10
+ # it would also be used for other imports. So, we make a module name that
11
+ # depends on the path for it to be unique using the hex-encoded hash of
12
+ # the path.
13
+ path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
+ module_name = path_hash
15
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
16
+ if spec is None:
17
+ raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
+ module = importlib.util.module_from_spec(spec)
19
+ if module is None:
20
+ raise ImportError(f"Cannot load module {module_name} from spec")
21
+ sys.modules[module_name] = module
22
+ spec.loader.exec_module(module) # type: ignore
23
+ return module
24
+
25
+
26
+ globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
build/torch213-cxx11-cu130-aarch64-linux/metadata.json ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "exact-solve",
3
+ "id": "_exact_solve_cuda_31a8fa7_dirty",
4
+ "version": 1,
5
+ "license": "Apache-2.0",
6
+ "python-depends": [],
7
+ "backend": {
8
+ "type": "cuda",
9
+ "archs": [
10
+ "10.0",
11
+ "12.0",
12
+ "8.0",
13
+ "8.6",
14
+ "8.9",
15
+ "9.0"
16
+ ]
17
+ },
18
+ "digest": {
19
+ "algorithm": "sha256",
20
+ "files": {
21
+ "__init__.py": "ZG5Z4fcpkB0UCWfChZwPg4dTmNtHpTxDCEvKPLJc9PI=",
22
+ "_exact_solve_cuda_31a8fa7_dirty.abi3.so": "q5CfyncrORNIsmcGBXkwfC9jl31c89HIwuFULSXc90A=",
23
+ "_exact_solve_recon.py": "gU5Ywz+UPfUeXS2dyzJ8NOOxgj7pgUcbFV9uAOAVIzU=",
24
+ "_ops.py": "1ujza9fF0nYcw94Evi6lqaZ8wNTrVB+GsP8zCO4FVj8=",
25
+ "exact_solve/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
26
+ }
27
+ },
28
+ "provenance": {
29
+ "kernel-builder": {
30
+ "version": "0.17.0-dev0",
31
+ "sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
32
+ "dirty": false
33
+ },
34
+ "kernel": {
35
+ "sha": "31a8fa724c4e4c412e04efba7ee534c01afb61cd",
36
+ "dirty": true
37
+ }
38
+ }
39
+ }