CUDA acceleration
Browse files- __init__.py +4 -0
- __main__.py +3 -0
- config.py +3 -0
- cuda_backend.py +1214 -0
- gui.py +15 -0
- pipeline.py +42 -28
- sieve.py +1 -3
- viterbi.py +68 -67
__init__.py
CHANGED
|
@@ -13,6 +13,7 @@ from .analog import (
|
|
| 13 |
vesica_gram_analog,
|
| 14 |
)
|
| 15 |
from .config import HPCConfig
|
|
|
|
| 16 |
from .factored import (
|
| 17 |
FactorResult,
|
| 18 |
factor_1d,
|
|
@@ -26,6 +27,9 @@ from .q2k import Q2KBlock, QK_K, dequantize_params, pack_codes, unpack_codes
|
|
| 26 |
|
| 27 |
__all__ = [
|
| 28 |
"HPCConfig",
|
|
|
|
|
|
|
|
|
|
| 29 |
"FactorResult",
|
| 30 |
"Q2KBlock",
|
| 31 |
"Q2KQuantizer",
|
|
|
|
| 13 |
vesica_gram_analog,
|
| 14 |
)
|
| 15 |
from .config import HPCConfig
|
| 16 |
+
from .cuda_backend import cuda_device_name, cuda_is_available, quantize_cuda
|
| 17 |
from .factored import (
|
| 18 |
FactorResult,
|
| 19 |
factor_1d,
|
|
|
|
| 27 |
|
| 28 |
__all__ = [
|
| 29 |
"HPCConfig",
|
| 30 |
+
"cuda_device_name",
|
| 31 |
+
"cuda_is_available",
|
| 32 |
+
"quantize_cuda",
|
| 33 |
"FactorResult",
|
| 34 |
"Q2KBlock",
|
| 35 |
"Q2KQuantizer",
|
__main__.py
CHANGED
|
@@ -40,6 +40,8 @@ def _config_from_args(args) -> HPCConfig:
|
|
| 40 |
cfg.n_cand_m = args.cand_m
|
| 41 |
if args.workers is not None:
|
| 42 |
cfg.workers = args.workers
|
|
|
|
|
|
|
| 43 |
if args.mse:
|
| 44 |
cfg.optimizer = "mse"
|
| 45 |
if args.no_spectral:
|
|
@@ -226,6 +228,7 @@ def main() -> None:
|
|
| 226 |
parser.add_argument("--cand-d", type=int, default=None, help="candidate d count")
|
| 227 |
parser.add_argument("--cand-m", type=int, default=None, help="candidate min count")
|
| 228 |
parser.add_argument("--workers", type=int, default=None, help="worker processes (0=auto CPU cores, 1=single)")
|
|
|
|
| 229 |
parser.add_argument("--mse", action="store_true", help="disable HPC sequence search")
|
| 230 |
parser.add_argument("--no-spectral", action="store_true", help="disable spectral/DC objective")
|
| 231 |
parser.add_argument("--verbose", action="store_true")
|
|
|
|
| 40 |
cfg.n_cand_m = args.cand_m
|
| 41 |
if args.workers is not None:
|
| 42 |
cfg.workers = args.workers
|
| 43 |
+
if args.device is not None:
|
| 44 |
+
cfg.device = args.device
|
| 45 |
if args.mse:
|
| 46 |
cfg.optimizer = "mse"
|
| 47 |
if args.no_spectral:
|
|
|
|
| 228 |
parser.add_argument("--cand-d", type=int, default=None, help="candidate d count")
|
| 229 |
parser.add_argument("--cand-m", type=int, default=None, help="candidate min count")
|
| 230 |
parser.add_argument("--workers", type=int, default=None, help="worker processes (0=auto CPU cores, 1=single)")
|
| 231 |
+
parser.add_argument("--device", default=None, help="compute device: auto (default), cuda, or cpu")
|
| 232 |
parser.add_argument("--mse", action="store_true", help="disable HPC sequence search")
|
| 233 |
parser.add_argument("--no-spectral", action="store_true", help="disable spectral/DC objective")
|
| 234 |
parser.add_argument("--verbose", action="store_true")
|
config.py
CHANGED
|
@@ -76,6 +76,7 @@ class HPCConfig:
|
|
| 76 |
clamp_sigma_hard: float = 4.0
|
| 77 |
|
| 78 |
# ---- reporting & parallelism ----------------------------------------
|
|
|
|
| 79 |
workers: int = 0
|
| 80 |
verbose: bool = False
|
| 81 |
|
|
@@ -112,4 +113,6 @@ class HPCConfig:
|
|
| 112 |
raise ValueError("Viterbi weights must be non-negative")
|
| 113 |
if self.workers < 0:
|
| 114 |
raise ValueError("workers must be >= 0 (0 = auto CPU count)")
|
|
|
|
|
|
|
| 115 |
return self
|
|
|
|
| 76 |
clamp_sigma_hard: float = 4.0
|
| 77 |
|
| 78 |
# ---- reporting & parallelism ----------------------------------------
|
| 79 |
+
device: str = "auto"
|
| 80 |
workers: int = 0
|
| 81 |
verbose: bool = False
|
| 82 |
|
|
|
|
| 113 |
raise ValueError("Viterbi weights must be non-negative")
|
| 114 |
if self.workers < 0:
|
| 115 |
raise ValueError("workers must be >= 0 (0 = auto CPU count)")
|
| 116 |
+
if self.device not in ("auto", "cuda", "cpu") and not self.device.startswith("cuda:"):
|
| 117 |
+
raise ValueError("device must be 'auto', 'cuda', 'cuda:N', or 'cpu'")
|
| 118 |
return self
|
cuda_backend.py
ADDED
|
@@ -0,0 +1,1214 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""CUDA GPU-accelerated backend for HPC-Quantize V2 (PyTorch CUDA).
|
| 2 |
+
|
| 3 |
+
Executes the entire HPC-Quantize V2 pipeline in batched FP32 GPU tensor kernels:
|
| 4 |
+
|
| 5 |
+
* Batched GGML seed solvers (``make_qkx2``, ``make_qp``) and 5-step WLS;
|
| 6 |
+
* Batched Geometry-Preserving Weight Analogs (Sub-block Iso-Moment,
|
| 7 |
+
Vesica Gram-Preserving, and Cross-Sub-block Scale-Lattice);
|
| 8 |
+
* Batched Coupled Orthogonal-Basis Bilinear Q2_K Factorization with rank-1
|
| 9 |
+
1-D integer refinement and 36-seed candidate lattice evaluation;
|
| 10 |
+
* Sequential 6-state Sieve & 36-state Viterbi DP + vectorized row DC carry;
|
| 11 |
+
* Batched Algebraic Phase-4 Coupled Assembly, Vesica+DC augmented 2x2 Cramer
|
| 12 |
+
(d, dmin) solve, O(1) quadratic fp16-ULP polish, closed-form 1-D quadratic
|
| 13 |
+
Vesica/DC code projection, and row-parallel GPU DC residual nudge.
|
| 14 |
+
"""
|
| 15 |
+
|
| 16 |
+
from __future__ import annotations
|
| 17 |
+
|
| 18 |
+
import time
|
| 19 |
+
import numpy as np
|
| 20 |
+
|
| 21 |
+
try:
|
| 22 |
+
import torch
|
| 23 |
+
except ImportError: # pragma: no cover
|
| 24 |
+
torch = None # type: ignore[assignment]
|
| 25 |
+
|
| 26 |
+
from .config import HPCConfig
|
| 27 |
+
from .factored import MULTS_D, MULTS_M, N_CAND_D, N_CAND_M, TOTAL_CANDIDATES
|
| 28 |
+
from .pipeline import QuantizedTensor, TensorStats, _phase3_select
|
| 29 |
+
from .q2k import NS, QK_K, Q2KBlock
|
| 30 |
+
from .seeds import seed_clamp_value
|
| 31 |
+
from .spectral import fold_weights
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def cuda_is_available() -> bool:
|
| 35 |
+
return bool(torch is not None and torch.cuda.is_available())
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def cuda_device_name(device: str = "cuda") -> str:
|
| 39 |
+
if not cuda_is_available():
|
| 40 |
+
return "CPU"
|
| 41 |
+
idx = torch.device(device).index or 0
|
| 42 |
+
return str(torch.cuda.get_device_name(idx))
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def _nearest_int_t(val: "torch.Tensor") -> "torch.Tensor":
|
| 46 |
+
"""Bit-exact GGML round-to-nearest-even on GPU."""
|
| 47 |
+
shifted = val.to(torch.float32) + 12582912.0
|
| 48 |
+
bits = shifted.view(torch.int32)
|
| 49 |
+
return ((bits & 0x007FFFFF) - 0x00400000).to(torch.int64)
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def _round_fp16_t(val: "torch.Tensor") -> "torch.Tensor":
|
| 53 |
+
return val.to(torch.float16).to(torch.float32)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def _to_fp16_bits_t(val: "torch.Tensor") -> "torch.Tensor":
|
| 57 |
+
return val.to(torch.float16).view(torch.int16).to(torch.int32) & 0xFFFF
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def _from_fp16_bits_t(bits: "torch.Tensor") -> "torch.Tensor":
|
| 61 |
+
return bits.to(torch.int16).view(torch.float16).to(torch.float32)
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def _codes_from_params_t(
|
| 65 |
+
X: "torch.Tensor",
|
| 66 |
+
d: "torch.Tensor",
|
| 67 |
+
m: "torch.Tensor",
|
| 68 |
+
ls: "torch.Tensor",
|
| 69 |
+
lm: "torch.Tensor",
|
| 70 |
+
) -> "torch.Tensor":
|
| 71 |
+
d_sub = (d[:, None].to(torch.float32) * ls.to(torch.float32)).repeat_interleave(16, dim=1)
|
| 72 |
+
m_sub = (m[:, None].to(torch.float32) * lm.to(torch.float32)).repeat_interleave(16, dim=1)
|
| 73 |
+
safe = d_sub >= 1.0e-15
|
| 74 |
+
safe_d = torch.where(safe, d_sub, torch.ones_like(d_sub))
|
| 75 |
+
q = _nearest_int_t((X.to(torch.float32) + m_sub) / safe_d).clamp(0, 3)
|
| 76 |
+
return torch.where(safe, q, torch.zeros_like(q)).to(torch.uint8)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def _dequantize_t(
|
| 80 |
+
d: "torch.Tensor",
|
| 81 |
+
m: "torch.Tensor",
|
| 82 |
+
ls: "torch.Tensor",
|
| 83 |
+
lm: "torch.Tensor",
|
| 84 |
+
codes: "torch.Tensor",
|
| 85 |
+
) -> "torch.Tensor":
|
| 86 |
+
d_sub = (d[:, None].to(torch.float32) * ls.to(torch.float32)).repeat_interleave(16, dim=1)
|
| 87 |
+
m_sub = (m[:, None].to(torch.float32) * lm.to(torch.float32)).repeat_interleave(16, dim=1)
|
| 88 |
+
return d_sub * codes.to(torch.float32) - m_sub
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def _objective_t(
|
| 92 |
+
X: "torch.Tensor",
|
| 93 |
+
W: "torch.Tensor",
|
| 94 |
+
d: "torch.Tensor",
|
| 95 |
+
m: "torch.Tensor",
|
| 96 |
+
ls: "torch.Tensor",
|
| 97 |
+
lm: "torch.Tensor",
|
| 98 |
+
codes: "torch.Tensor",
|
| 99 |
+
carry: "torch.Tensor | None",
|
| 100 |
+
cfg: HPCConfig,
|
| 101 |
+
) -> tuple["torch.Tensor", "torch.Tensor"]:
|
| 102 |
+
xh = _dequantize_t(d, m, ls, lm, codes)
|
| 103 |
+
e = X - xh
|
| 104 |
+
sse = torch.sum(W * e * e, dim=1, dtype=torch.float32)
|
| 105 |
+
if not cfg.use_spectral or (cfg.dc_lambda == 0.0 and cfg.vw_lambda == 0.0):
|
| 106 |
+
return sse, sse
|
| 107 |
+
fw = fold_weights(QK_K, cfg)
|
| 108 |
+
pen = torch.zeros_like(sse)
|
| 109 |
+
src = e
|
| 110 |
+
m_len = QK_K // 2
|
| 111 |
+
lvl = 0
|
| 112 |
+
while m_len >= 1:
|
| 113 |
+
dst = src[:, :m_len] + src[:, m_len : 2 * m_len]
|
| 114 |
+
if m_len == 1 and carry is not None:
|
| 115 |
+
dst = dst + carry[:, None].to(torch.float32)
|
| 116 |
+
if fw[lvl] != 0.0:
|
| 117 |
+
pen = pen + float(fw[lvl]) * torch.sum(dst * dst, dim=1)
|
| 118 |
+
src = dst
|
| 119 |
+
m_len //= 2
|
| 120 |
+
lvl += 1
|
| 121 |
+
return sse + pen, sse
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def _make_qkx2_cuda(x: "torch.Tensor", w: "torch.Tensor", nmax: int = 3):
|
| 125 |
+
x = x.to(torch.float32)
|
| 126 |
+
w = w.to(torch.float32)
|
| 127 |
+
xmin = torch.clamp(torch.min(x, dim=1).values, max=0.0)
|
| 128 |
+
xmax = torch.max(x, dim=1).values
|
| 129 |
+
span = xmax - xmin
|
| 130 |
+
degenerate = (xmax == xmin) | (span <= 1.0e-30)
|
| 131 |
+
sum_w = torch.sum(w, dim=1)
|
| 132 |
+
sum_x = torch.sum(w * x, dim=1)
|
| 133 |
+
safe_span = torch.where(span > 1.0e-30, span, torch.ones_like(span))
|
| 134 |
+
iscale = nmax / safe_span
|
| 135 |
+
scale = 1.0 / iscale
|
| 136 |
+
codes = _nearest_int_t(iscale[:, None] * (x - xmin[:, None])).clamp(0, nmax)
|
| 137 |
+
best_mad = torch.sum(w * torch.abs(scale[:, None] * codes.to(torch.float32) + xmin[:, None] - x), dim=1)
|
| 138 |
+
best_scale = scale.clone()
|
| 139 |
+
best_min = xmin.clone()
|
| 140 |
+
best_codes = codes.clone()
|
| 141 |
+
|
| 142 |
+
steps = torch.arange(16, dtype=torch.float32, device=x.device)[None, :] # (1, 16)
|
| 143 |
+
try_iscale = (-0.5 + 0.1 * steps + nmax) / safe_span[:, None] # (N, 16)
|
| 144 |
+
aux = _nearest_int_t(try_iscale[:, :, None] * (x - xmin[:, None])[:, None, :]).clamp(0, nmax)
|
| 145 |
+
aux_f = aux.to(torch.float32)
|
| 146 |
+
w_e = w[:, None, :]
|
| 147 |
+
x_e = x[:, None, :]
|
| 148 |
+
sl = torch.sum(w_e * aux_f, dim=2)
|
| 149 |
+
sl2 = torch.sum(w_e * aux_f * aux_f, dim=2)
|
| 150 |
+
sxl = torch.sum(w_e * aux_f * x_e, dim=2)
|
| 151 |
+
det = sum_w[:, None] * sl2 - sl * sl
|
| 152 |
+
valid = det > 1.0e-30
|
| 153 |
+
s_det = torch.where(valid, det, torch.ones_like(det))
|
| 154 |
+
this_scale = (sum_w[:, None] * sxl - sum_x[:, None] * sl) / s_det
|
| 155 |
+
this_min = (sl2 * sum_x[:, None] - sl * sxl) / s_det
|
| 156 |
+
pos_min = this_min > 0.0
|
| 157 |
+
this_min = torch.where(pos_min, torch.zeros_like(this_min), this_min)
|
| 158 |
+
s_sl2 = torch.where(sl2 > 1.0e-30, sl2, torch.ones_like(sl2))
|
| 159 |
+
this_scale = torch.where(pos_min & (sl2 > 1.0e-30), sxl / s_sl2, this_scale)
|
| 160 |
+
mad = torch.sum(w_e * torch.abs(this_scale[:, :, None] * aux_f + this_min[:, :, None] - x_e), dim=2)
|
| 161 |
+
mad = torch.where(valid, mad, torch.full_like(mad, float("inf")))
|
| 162 |
+
min_mad, best_k = torch.min(mad, dim=1)
|
| 163 |
+
take = min_mad < best_mad
|
| 164 |
+
rows = torch.arange(x.shape[0], device=x.device)
|
| 165 |
+
best_scale = torch.where(take, this_scale[rows, best_k], best_scale)
|
| 166 |
+
best_min = torch.where(take, this_min[rows, best_k], best_min)
|
| 167 |
+
best_codes = torch.where(take[:, None], aux[rows, best_k], best_codes)
|
| 168 |
+
|
| 169 |
+
best_scale = torch.where(degenerate, torch.zeros_like(best_scale), best_scale)
|
| 170 |
+
best_codes = torch.where(degenerate[:, None], torch.zeros_like(best_codes), best_codes)
|
| 171 |
+
best_min = torch.where(degenerate, xmin, best_min)
|
| 172 |
+
return best_scale, -best_min, best_codes.to(torch.uint8)
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
def _make_qp_cuda(values: "torch.Tensor", weights: "torch.Tensor", nmax: int = 15):
|
| 176 |
+
x = values.to(torch.float32)
|
| 177 |
+
w = weights.to(torch.float32)
|
| 178 |
+
xmax = torch.max(x, dim=1).values
|
| 179 |
+
dead = xmax < 1.0e-15
|
| 180 |
+
safe_max = torch.where(dead, torch.ones_like(xmax), xmax)
|
| 181 |
+
|
| 182 |
+
steps = torch.arange(-4, 5, dtype=torch.float32, device=x.device)[None, :] # (1, 9)
|
| 183 |
+
t_is = (0.1 * steps + nmax) / safe_max[:, None] # (N, 9)
|
| 184 |
+
t_sc = 1.0 / t_is
|
| 185 |
+
t_cd = _nearest_int_t(t_is[:, :, None] * x[:, None, :]).clamp(0, nmax).to(torch.float32)
|
| 186 |
+
mse = torch.sum(w[:, None, :] * (x[:, None, :] - t_sc[:, :, None] * t_cd) ** 2, dim=2)
|
| 187 |
+
best_k = torch.argmin(mse, dim=1)
|
| 188 |
+
rows = torch.arange(x.shape[0], device=x.device)
|
| 189 |
+
best_iscale = t_is[rows, best_k]
|
| 190 |
+
|
| 191 |
+
codes = _nearest_int_t(best_iscale[:, None] * x).clamp(0, nmax)
|
| 192 |
+
cf = codes.to(torch.float32)
|
| 193 |
+
sumlx = torch.sum(w * x * cf, dim=1)
|
| 194 |
+
suml2 = torch.sum(w * cf * cf, dim=1)
|
| 195 |
+
|
| 196 |
+
for _ in range(3):
|
| 197 |
+
changed = False
|
| 198 |
+
for i in range(x.shape[1]):
|
| 199 |
+
wi = w[:, i]
|
| 200 |
+
xi = x[:, i]
|
| 201 |
+
li = codes[:, i].to(torch.float32)
|
| 202 |
+
slx = sumlx - wi * xi * li
|
| 203 |
+
sl2 = suml2 - wi * li * li
|
| 204 |
+
valid = (slx > 0.0) & (sl2 > 0.0)
|
| 205 |
+
s_slx = torch.where(valid, slx, torch.ones_like(slx))
|
| 206 |
+
new_l = _nearest_int_t(xi * sl2 / s_slx).clamp(0, nmax)
|
| 207 |
+
nl_f = new_l.to(torch.float32)
|
| 208 |
+
trial = valid & (new_l != codes[:, i])
|
| 209 |
+
slx2 = slx + wi * xi * nl_f
|
| 210 |
+
sl2_2 = sl2 + wi * nl_f * nl_f
|
| 211 |
+
better = trial & (slx2 * slx2 * suml2 > sumlx * sumlx * sl2_2)
|
| 212 |
+
if torch.any(better):
|
| 213 |
+
codes[:, i] = torch.where(better, new_l, codes[:, i])
|
| 214 |
+
sumlx = torch.where(better, slx2, sumlx)
|
| 215 |
+
suml2 = torch.where(better, sl2_2, suml2)
|
| 216 |
+
changed = True
|
| 217 |
+
if not changed:
|
| 218 |
+
break
|
| 219 |
+
|
| 220 |
+
s_l2 = torch.where(suml2 > 1.0e-30, suml2, torch.ones_like(suml2))
|
| 221 |
+
d = torch.where((suml2 > 1.0e-30) & (~dead), sumlx / s_l2, torch.zeros_like(sumlx))
|
| 222 |
+
codes = torch.where(dead[:, None], torch.zeros_like(codes), codes)
|
| 223 |
+
return d, codes.to(torch.uint8)
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
def _build_seeds_cuda(X: "torch.Tensor", W: "torch.Tensor | None", clamp_val: float):
|
| 227 |
+
B = X.shape[0]
|
| 228 |
+
blocks = X.reshape(B, 16, 16)
|
| 229 |
+
clipped = torch.clamp(blocks, min=-clamp_val, max=clamp_val)
|
| 230 |
+
if W is None:
|
| 231 |
+
sigma_sq = torch.mean(blocks * blocks, dim=(1, 2), keepdim=True)
|
| 232 |
+
weights = torch.sqrt(sigma_sq + clipped * clipped)
|
| 233 |
+
else:
|
| 234 |
+
weights = W.reshape(B, 16, 16)
|
| 235 |
+
scales, mins, codes = _make_qkx2_cuda(clipped.reshape(-1, 16), weights.reshape(-1, 16), 3)
|
| 236 |
+
scales = scales.reshape(B, 16)
|
| 237 |
+
mins = mins.reshape(B, 16)
|
| 238 |
+
codes = codes.reshape(B, 256)
|
| 239 |
+
sw = torch.sum(weights, dim=2)
|
| 240 |
+
d, ls = _make_qp_cuda(scales, sw, 15)
|
| 241 |
+
dmin, lm = _make_qp_cuda(mins, sw, 15)
|
| 242 |
+
return {
|
| 243 |
+
"scales": scales,
|
| 244 |
+
"mins": mins,
|
| 245 |
+
"codes": codes,
|
| 246 |
+
"sw": sw,
|
| 247 |
+
"d": d,
|
| 248 |
+
"dmin": dmin,
|
| 249 |
+
"ls": ls,
|
| 250 |
+
"lm": lm,
|
| 251 |
+
}
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
def _phase2_wls_cuda(X: "torch.Tensor", W: "torch.Tensor", seeds: dict, clamp_val: float):
|
| 255 |
+
clipped = torch.clamp(X, min=-clamp_val, max=clamp_val)
|
| 256 |
+
base_d = seeds["d"].clone()
|
| 257 |
+
base_m = seeds["dmin"].clone()
|
| 258 |
+
ls = seeds["ls"].clone()
|
| 259 |
+
lm = seeds["lm"].clone()
|
| 260 |
+
scales = seeds["scales"]
|
| 261 |
+
mins = seeds["mins"]
|
| 262 |
+
seed_d = seeds["d"]
|
| 263 |
+
seed_m = seeds["dmin"]
|
| 264 |
+
|
| 265 |
+
for _ in range(5):
|
| 266 |
+
codes = _codes_from_params_t(clipped, base_d, base_m, ls, lm).to(torch.float32)
|
| 267 |
+
A = ls.to(torch.float32).repeat_interleave(16, dim=1) * codes
|
| 268 |
+
Bm = lm.to(torch.float32).repeat_interleave(16, dim=1)
|
| 269 |
+
Saa = torch.sum(W * A * A, dim=1)
|
| 270 |
+
Sab = torch.sum(W * A * Bm, dim=1)
|
| 271 |
+
Sbb = torch.sum(W * Bm * Bm, dim=1)
|
| 272 |
+
Sxa = torch.sum(W * clipped * A, dim=1)
|
| 273 |
+
Sxb = torch.sum(W * clipped * Bm, dim=1)
|
| 274 |
+
det = Saa * Sbb - Sab * Sab
|
| 275 |
+
valid = det > 1.0e-30
|
| 276 |
+
s_det = torch.where(valid, det, torch.ones_like(det))
|
| 277 |
+
d_new = (Sbb * Sxa - Sab * Sxb) / s_det
|
| 278 |
+
m_new = (Sab * Sxa - Saa * Sxb) / s_det
|
| 279 |
+
upd_d = valid & (d_new > 0.0) & (d_new < 4.0 * (seed_d + 1.0e-10))
|
| 280 |
+
upd_m = valid & (m_new > 0.0) & (m_new < 4.0 * (seed_m + 1.0e-10))
|
| 281 |
+
base_d = torch.where(upd_d, _round_fp16_t(d_new), base_d)
|
| 282 |
+
base_m = torch.where(upd_m, _round_fp16_t(m_new), base_m)
|
| 283 |
+
sd = torch.where(base_d > 1.0e-15, base_d, torch.ones_like(base_d))
|
| 284 |
+
sm = torch.where(base_m > 1.0e-15, base_m, torch.ones_like(base_m))
|
| 285 |
+
ls = torch.where(
|
| 286 |
+
base_d[:, None] > 1.0e-15,
|
| 287 |
+
_nearest_int_t(scales / sd[:, None]).clamp(0, 15),
|
| 288 |
+
torch.zeros_like(ls, dtype=torch.int64),
|
| 289 |
+
).to(torch.uint8)
|
| 290 |
+
lm = torch.where(
|
| 291 |
+
base_m[:, None] > 1.0e-15,
|
| 292 |
+
_nearest_int_t(mins / sm[:, None]).clamp(0, 15),
|
| 293 |
+
torch.zeros_like(lm, dtype=torch.int64),
|
| 294 |
+
).to(torch.uint8)
|
| 295 |
+
|
| 296 |
+
return base_d, base_m
|
| 297 |
+
|
| 298 |
+
|
| 299 |
+
def _factor_1d_cuda(
|
| 300 |
+
V: "torch.Tensor",
|
| 301 |
+
W: "torch.Tensor",
|
| 302 |
+
nmax: int = 15,
|
| 303 |
+
iters: int = 5,
|
| 304 |
+
refine: bool = True,
|
| 305 |
+
) -> tuple["torch.Tensor", "torch.Tensor"]:
|
| 306 |
+
"""Batched FP32 1-D factorization across B blocks on GPU with rank-1 sweep."""
|
| 307 |
+
V = torch.clamp(V.to(torch.float32), min=0.0)
|
| 308 |
+
W = torch.clamp(W.to(torch.float32), min=0.0)
|
| 309 |
+
B, n = V.shape
|
| 310 |
+
vmax = torch.max(V, dim=1).values
|
| 311 |
+
active = vmax >= 1.0e-30
|
| 312 |
+
d = torch.where(active, vmax / nmax, torch.zeros_like(vmax))
|
| 313 |
+
sd = torch.where(d > 1.0e-30, d, torch.ones_like(d))
|
| 314 |
+
codes = torch.where(
|
| 315 |
+
active[:, None] & (d[:, None] > 1.0e-30),
|
| 316 |
+
_nearest_int_t(V / sd[:, None]).clamp(0, nmax),
|
| 317 |
+
torch.zeros((B, n), dtype=torch.int64, device=V.device),
|
| 318 |
+
)
|
| 319 |
+
for _ in range(iters):
|
| 320 |
+
cf = codes.to(torch.float32)
|
| 321 |
+
den = torch.sum(W * cf * cf, dim=1)
|
| 322 |
+
valid = active & (den > 1.0e-30)
|
| 323 |
+
s_den = torch.where(valid, den, torch.ones_like(den))
|
| 324 |
+
dn = torch.sum(W * V * cf, dim=1) / s_den
|
| 325 |
+
d = torch.where(valid & (dn > 0.0), dn, d)
|
| 326 |
+
sd = torch.where(d > 1.0e-30, d, torch.ones_like(d))
|
| 327 |
+
codes = torch.where(
|
| 328 |
+
active[:, None] & (d[:, None] > 1.0e-30),
|
| 329 |
+
_nearest_int_t(V / sd[:, None]).clamp(0, nmax),
|
| 330 |
+
torch.zeros_like(codes),
|
| 331 |
+
)
|
| 332 |
+
|
| 333 |
+
if refine:
|
| 334 |
+
opts = torch.arange(nmax + 1, dtype=torch.float32, device=V.device)[None, :]
|
| 335 |
+
opts_sq = opts * opts
|
| 336 |
+
wv = W * V
|
| 337 |
+
cf = codes.to(torch.float32)
|
| 338 |
+
sum_lv = torch.sum(wv * cf, dim=1)
|
| 339 |
+
sum_l2 = torch.sum(W * cf * cf, dim=1)
|
| 340 |
+
s_l2 = torch.where(sum_l2 > 1.0e-30, sum_l2, torch.ones_like(sum_l2))
|
| 341 |
+
best_gain = torch.where(sum_l2 > 1.0e-30, (sum_lv * sum_lv) / s_l2, torch.zeros_like(sum_lv))
|
| 342 |
+
rows = torch.arange(B, device=V.device)
|
| 343 |
+
|
| 344 |
+
for i in range(n):
|
| 345 |
+
wi = W[:, i : i + 1]
|
| 346 |
+
wvi = wv[:, i : i + 1]
|
| 347 |
+
li = codes[:, i : i + 1].to(torch.float32)
|
| 348 |
+
slv = (sum_lv[:, None] - wvi * li) + wvi * opts
|
| 349 |
+
sl2 = (sum_l2[:, None] - wi * (li * li)) + wi * opts_sq
|
| 350 |
+
valid = (wi > 0.0) & (sl2 > 1.0e-30) & (slv > 0.0)
|
| 351 |
+
gains = torch.where(valid, (slv * slv) / torch.where(valid, sl2, torch.ones_like(sl2)), -torch.ones_like(slv))
|
| 352 |
+
max_g, k_idx = torch.max(gains, dim=1)
|
| 353 |
+
improve = active & (k_idx != codes[:, i]) & (max_g > best_gain + 1.0e-12)
|
| 354 |
+
if torch.any(improve):
|
| 355 |
+
best_gain = torch.where(improve, max_g, best_gain)
|
| 356 |
+
sum_lv = torch.where(improve, slv[rows, k_idx], sum_lv)
|
| 357 |
+
sum_l2 = torch.where(improve, sl2[rows, k_idx], sum_l2)
|
| 358 |
+
codes[:, i] = torch.where(improve, k_idx, codes[:, i])
|
| 359 |
+
|
| 360 |
+
s_l2 = torch.where(sum_l2 > 1.0e-30, sum_l2, torch.ones_like(sum_l2))
|
| 361 |
+
d = torch.where(active & (sum_l2 > 1.0e-30), sum_lv / s_l2, d)
|
| 362 |
+
|
| 363 |
+
return d, codes.to(torch.uint8)
|
| 364 |
+
|
| 365 |
+
|
| 366 |
+
def _analogs_cuda(X: "torch.Tensor", W: "torch.Tensor") -> list["torch.Tensor"]:
|
| 367 |
+
B = X.shape[0]
|
| 368 |
+
xb = X.reshape(B, NS, 16)
|
| 369 |
+
wb = W.reshape(B, NS, 16)
|
| 370 |
+
wsum = torch.clamp(torch.sum(wb, dim=2, keepdim=True), min=1.0e-30)
|
| 371 |
+
mu = torch.sum(wb * xb, dim=2, keepdim=True) / wsum
|
| 372 |
+
centered = xb - mu
|
| 373 |
+
var = torch.sum(wb * centered * centered, dim=2, keepdim=True) / wsum
|
| 374 |
+
sigma = torch.sqrt(torch.clamp(var, min=1.0e-30))
|
| 375 |
+
active = sigma >= 1.0e-15
|
| 376 |
+
z = centered / sigma
|
| 377 |
+
q_ideal = torch.linspace(-1.35, 1.35, 16, dtype=torch.float32, device=X.device)
|
| 378 |
+
order = torch.argsort(z, dim=2, stable=True)
|
| 379 |
+
z_quant = torch.empty_like(z)
|
| 380 |
+
z_quant.scatter_(2, order, q_ideal[None, None, :].expand_as(z))
|
| 381 |
+
|
| 382 |
+
analogs = []
|
| 383 |
+
for tau, beta, q_blend in ((1.60, 0.25, 0.30), (2.15, 0.15, 0.15)):
|
| 384 |
+
w_rel = torch.clamp(wb / (wsum / 16.0), min=1.0e-12) ** beta
|
| 385 |
+
z_comp = tau * torch.tanh((z * w_rel) / tau)
|
| 386 |
+
z_a = (1.0 - q_blend) * z_comp + q_blend * z_quant
|
| 387 |
+
mu_a = torch.sum(wb * z_a, dim=2, keepdim=True) / wsum
|
| 388 |
+
z_a = z_a - mu_a
|
| 389 |
+
var_a = torch.sum(wb * z_a * z_a, dim=2, keepdim=True) / wsum
|
| 390 |
+
scale = torch.where(var_a > 1.0e-30, torch.sqrt(var / torch.clamp(var_a, min=1.0e-30)), torch.zeros_like(var_a))
|
| 391 |
+
sub_a = torch.where(active, mu + z_a * scale, xb)
|
| 392 |
+
orig_min = torch.min(xb, dim=2, keepdim=True).values
|
| 393 |
+
new_min = torch.min(sub_a, dim=2, keepdim=True).values
|
| 394 |
+
shift = torch.where((orig_min <= 0.0) & (new_min > 0.0), new_min, torch.zeros_like(new_min))
|
| 395 |
+
analogs.append((sub_a - shift).reshape(B, QK_K))
|
| 396 |
+
|
| 397 |
+
return analogs
|
| 398 |
+
|
| 399 |
+
|
| 400 |
+
def _sub_ls_cuda(X: "torch.Tensor", W: "torch.Tensor", codes: "torch.Tensor"):
|
| 401 |
+
B = X.shape[0]
|
| 402 |
+
q = codes.to(torch.float32).reshape(B, NS, 16)
|
| 403 |
+
xb = X.reshape(B, NS, 16)
|
| 404 |
+
wb = W.reshape(B, NS, 16)
|
| 405 |
+
Sqq = torch.sum(wb * q * q, dim=2)
|
| 406 |
+
Swq = torch.sum(wb * q, dim=2)
|
| 407 |
+
Sw = torch.sum(wb, dim=2)
|
| 408 |
+
Sqx = torch.sum(wb * q * xb, dim=2)
|
| 409 |
+
Sx = torch.sum(wb * xb, dim=2)
|
| 410 |
+
det = Sqq * Sw - Swq * Swq
|
| 411 |
+
valid = det > 1.0e-30
|
| 412 |
+
s_det = torch.where(valid, det, torch.ones_like(det))
|
| 413 |
+
s_sw = torch.where(Sw > 1.0e-30, Sw, torch.ones_like(Sw))
|
| 414 |
+
sj = torch.clamp(torch.where(valid, (Sqx * Sw - Swq * Sx) / s_det, torch.zeros_like(det)), min=0.0)
|
| 415 |
+
tj = torch.where(valid, (sj * Swq - Sx) / s_sw, torch.zeros_like(det))
|
| 416 |
+
bad_tj = tj < 0.0
|
| 417 |
+
tj = torch.where(bad_tj, torch.zeros_like(tj), tj)
|
| 418 |
+
s_sqq = torch.where(Sqq > 1.0e-30, Sqq, torch.ones_like(Sqq))
|
| 419 |
+
fallback = torch.clamp(torch.where(Sqq > 1.0e-30, Sqx / s_sqq, torch.zeros_like(Sqq)), min=0.0)
|
| 420 |
+
sj = torch.where(bad_tj, fallback, sj)
|
| 421 |
+
return sj, tj, Sqq, Swq, Sw, Sx
|
| 422 |
+
|
| 423 |
+
|
| 424 |
+
def _factor_candidates_cuda(
|
| 425 |
+
X: "torch.Tensor",
|
| 426 |
+
W: "torch.Tensor",
|
| 427 |
+
scales: "torch.Tensor",
|
| 428 |
+
mins: "torch.Tensor",
|
| 429 |
+
base_d: "torch.Tensor",
|
| 430 |
+
base_m: "torch.Tensor",
|
| 431 |
+
cfg: HPCConfig,
|
| 432 |
+
) -> np.ndarray:
|
| 433 |
+
"""Vectorized FP32 36-seed candidate lattice across B blocks on GPU."""
|
| 434 |
+
B = X.shape[0]
|
| 435 |
+
device = X.device
|
| 436 |
+
nd, nm = int(cfg.n_cand_d), int(cfg.n_cand_m)
|
| 437 |
+
di_idx = [a if nd == N_CAND_D else (a * N_CAND_D) // nd for a in range(nd)]
|
| 438 |
+
mi_idx = [b if nm == N_CAND_M else (b * N_CAND_M) // nm for b in range(nm)]
|
| 439 |
+
md = torch.tensor(MULTS_D[di_idx], dtype=torch.float32, device=device)
|
| 440 |
+
mm = torch.tensor(MULTS_M[mi_idx], dtype=torch.float32, device=device)
|
| 441 |
+
|
| 442 |
+
ds_mat = _round_fp16_t(base_d[:, None] * md[None, :])
|
| 443 |
+
ms_mat = _round_fp16_t(base_m[:, None] * mm[None, :])
|
| 444 |
+
ds = ds_mat.repeat_interleave(nm, dim=1)
|
| 445 |
+
ms = ms_mat.repeat(1, nd)
|
| 446 |
+
K = ds.shape[1]
|
| 447 |
+
|
| 448 |
+
sc = scales[:, None, :]
|
| 449 |
+
mn = mins[:, None, :]
|
| 450 |
+
s_ds = torch.where(ds[:, :, None] > 1.0e-30, ds[:, :, None], torch.ones_like(ds[:, :, None]))
|
| 451 |
+
s_ms = torch.where(ms[:, :, None] > 1.0e-30, ms[:, :, None], torch.ones_like(ms[:, :, None]))
|
| 452 |
+
ls0 = torch.where(
|
| 453 |
+
ds[:, :, None] > 1.0e-30,
|
| 454 |
+
_nearest_int_t(sc / s_ds).clamp(0, 15),
|
| 455 |
+
torch.zeros((B, K, NS), dtype=torch.int64, device=device),
|
| 456 |
+
).to(torch.float32)
|
| 457 |
+
lm0 = torch.where(
|
| 458 |
+
ms[:, :, None] > 1.0e-30,
|
| 459 |
+
_nearest_int_t(mn / s_ms).clamp(0, 15),
|
| 460 |
+
torch.zeros((B, K, NS), dtype=torch.int64, device=device),
|
| 461 |
+
).to(torch.float32)
|
| 462 |
+
|
| 463 |
+
X_e = X[:, None, :]
|
| 464 |
+
W_e = W[:, None, :]
|
| 465 |
+
fw = fold_weights(QK_K, cfg)
|
| 466 |
+
|
| 467 |
+
def _eval_k(ls_k: "torch.Tensor", lm_k: "torch.Tensor"):
|
| 468 |
+
d_sub = (ds[:, :, None] * ls_k).repeat_interleave(16, dim=2)
|
| 469 |
+
m_sub = (ms[:, :, None] * lm_k).repeat_interleave(16, dim=2)
|
| 470 |
+
safe = d_sub >= 1.0e-15
|
| 471 |
+
sd = torch.where(safe, d_sub, torch.ones_like(d_sub))
|
| 472 |
+
q = torch.where(
|
| 473 |
+
safe,
|
| 474 |
+
_nearest_int_t((X_e + m_sub) / sd).clamp(0, 3),
|
| 475 |
+
torch.zeros((B, K, QK_K), dtype=torch.int64, device=device),
|
| 476 |
+
).to(torch.float32)
|
| 477 |
+
e = X_e - (d_sub * q - m_sub)
|
| 478 |
+
sse = torch.sum(W_e * e * e, dim=2)
|
| 479 |
+
if cfg.use_spectral and (cfg.dc_lambda != 0.0 or cfg.vw_lambda != 0.0):
|
| 480 |
+
pen = torch.zeros_like(sse)
|
| 481 |
+
src = e
|
| 482 |
+
m_len = QK_K // 2
|
| 483 |
+
lvl = 0
|
| 484 |
+
while m_len >= 1:
|
| 485 |
+
dst = src[:, :, :m_len] + src[:, :, m_len : 2 * m_len]
|
| 486 |
+
if fw[lvl] != 0.0:
|
| 487 |
+
pen = pen + float(fw[lvl]) * torch.sum(dst * dst, dim=2)
|
| 488 |
+
src = dst
|
| 489 |
+
m_len //= 2
|
| 490 |
+
lvl += 1
|
| 491 |
+
return sse + pen, q
|
| 492 |
+
return sse, q
|
| 493 |
+
|
| 494 |
+
obj0, q0 = _eval_k(ls0, lm0)
|
| 495 |
+
q_sub = q0.reshape(B, K, NS, 16)
|
| 496 |
+
xb = X.reshape(B, 1, NS, 16)
|
| 497 |
+
wb = W.reshape(B, 1, NS, 16)
|
| 498 |
+
Sqq = torch.sum(wb * q_sub * q_sub, dim=3)
|
| 499 |
+
Swq = torch.sum(wb * q_sub, dim=3)
|
| 500 |
+
Sw = torch.sum(wb, dim=3)
|
| 501 |
+
Sqx = torch.sum(wb * q_sub * xb, dim=3)
|
| 502 |
+
Sx = torch.sum(wb * xb, dim=3)
|
| 503 |
+
det = Sqq * Sw - Swq * Swq
|
| 504 |
+
valid = det > 1.0e-30
|
| 505 |
+
s_det = torch.where(valid, det, torch.ones_like(det))
|
| 506 |
+
s_sw = torch.where(Sw > 1.0e-30, Sw, torch.ones_like(Sw))
|
| 507 |
+
sj = torch.clamp(torch.where(valid, (Sqx * Sw - Swq * Sx) / s_det, torch.zeros_like(det)), min=0.0)
|
| 508 |
+
tj = torch.where(valid, (sj * Swq - Sx) / s_sw, torch.zeros_like(det))
|
| 509 |
+
bad_tj = tj < 0.0
|
| 510 |
+
tj = torch.where(bad_tj, torch.zeros_like(tj), tj)
|
| 511 |
+
s_sqq = torch.where(Sqq > 1.0e-30, Sqq, torch.ones_like(Sqq))
|
| 512 |
+
sj = torch.where(bad_tj, torch.clamp(torch.where(Sqq > 1.0e-30, Sqx / s_sqq, torch.zeros_like(Sqq)), min=0.0), sj)
|
| 513 |
+
|
| 514 |
+
ls1 = torch.where(
|
| 515 |
+
ds[:, :, None] > 1.0e-30,
|
| 516 |
+
_nearest_int_t(sj / s_ds).clamp(0, 15),
|
| 517 |
+
torch.zeros((B, K, NS), dtype=torch.int64, device=device),
|
| 518 |
+
).to(torch.float32)
|
| 519 |
+
t_tgt = torch.clamp((ds[:, :, None] * ls1 * Swq - Sx) / s_sw, min=0.0) if cfg.factor_coupled_q2k else tj
|
| 520 |
+
lm1 = torch.where(
|
| 521 |
+
ms[:, :, None] > 1.0e-30,
|
| 522 |
+
_nearest_int_t(t_tgt / s_ms).clamp(0, 15),
|
| 523 |
+
torch.zeros((B, K, NS), dtype=torch.int64, device=device),
|
| 524 |
+
).to(torch.float32)
|
| 525 |
+
obj1, _ = _eval_k(ls1, lm1)
|
| 526 |
+
best_k = torch.minimum(obj0, obj1).cpu().numpy()
|
| 527 |
+
|
| 528 |
+
out = np.full((B, TOTAL_CANDIDATES), np.inf, dtype=np.float32)
|
| 529 |
+
if nd == N_CAND_D and nm == N_CAND_M:
|
| 530 |
+
out[:, :] = best_k
|
| 531 |
+
else:
|
| 532 |
+
for a, di in enumerate(di_idx):
|
| 533 |
+
cb = int(di) >> 2
|
| 534 |
+
for b, mi in enumerate(mi_idx):
|
| 535 |
+
fb = int(mi) >> 2
|
| 536 |
+
col = best_k[:, a * nm + b]
|
| 537 |
+
for aa in range(4):
|
| 538 |
+
for bb in range(4):
|
| 539 |
+
idx = (cb * 4 + aa) * N_CAND_M + fb * 4 + bb
|
| 540 |
+
out[:, idx] = np.minimum(out[:, idx], col)
|
| 541 |
+
return out
|
| 542 |
+
|
| 543 |
+
|
| 544 |
+
def _standalone_coupled_solve_cuda(
|
| 545 |
+
X: "torch.Tensor", W: "torch.Tensor", cfg: HPCConfig
|
| 546 |
+
) -> tuple["torch.Tensor", "torch.Tensor", "torch.Tensor", "torch.Tensor", "torch.Tensor", "torch.Tensor"]:
|
| 547 |
+
B = X.shape[0]
|
| 548 |
+
Sw = torch.sum(W.reshape(B, NS, 16), dim=2)
|
| 549 |
+
s_Sw = torch.clamp(Sw, min=1.0e-30)
|
| 550 |
+
|
| 551 |
+
sources = [X]
|
| 552 |
+
if cfg.use_weight_analogs:
|
| 553 |
+
sources.extend(_analogs_cuda(X, W))
|
| 554 |
+
|
| 555 |
+
best_obj = torch.full((B,), float("inf"), dtype=torch.float32, device=X.device)
|
| 556 |
+
best_d = torch.zeros((B,), dtype=torch.float32, device=X.device)
|
| 557 |
+
best_m = torch.zeros((B,), dtype=torch.float32, device=X.device)
|
| 558 |
+
best_ls = torch.zeros((B, NS), dtype=torch.uint8, device=X.device)
|
| 559 |
+
best_lm = torch.zeros((B, NS), dtype=torch.uint8, device=X.device)
|
| 560 |
+
best_codes = torch.zeros((B, QK_K), dtype=torch.uint8, device=X.device)
|
| 561 |
+
|
| 562 |
+
for src in sources:
|
| 563 |
+
sb = src.reshape(B, NS, 16)
|
| 564 |
+
xmin = torch.clamp(torch.min(sb, dim=2).values, max=0.0)
|
| 565 |
+
xmax = torch.max(sb, dim=2).values
|
| 566 |
+
span = xmax - xmin
|
| 567 |
+
s0 = torch.where(span > 1.0e-30, span / 3.0, torch.zeros_like(span))
|
| 568 |
+
t0 = -xmin
|
| 569 |
+
s_s0 = torch.where(s0 > 1.0e-30, s0, torch.ones_like(s0))
|
| 570 |
+
q0 = torch.where(
|
| 571 |
+
s0[:, :, None] > 1.0e-30,
|
| 572 |
+
_nearest_int_t((sb + t0[:, :, None]) / s_s0[:, :, None]).clamp(0, 3),
|
| 573 |
+
torch.zeros((B, NS, 16), dtype=torch.int64, device=X.device),
|
| 574 |
+
).reshape(B, QK_K).to(torch.uint8)
|
| 575 |
+
|
| 576 |
+
codes = q0
|
| 577 |
+
for _ in range(3):
|
| 578 |
+
s, t, Sqq, Swq, _, Sx = _sub_ls_cuda(X, W, codes)
|
| 579 |
+
q_bar = Swq / s_Sw
|
| 580 |
+
Vqq = torch.clamp(Sqq - Swq * q_bar, min=1.0e-12 * Sw)
|
| 581 |
+
d, ls = _factor_1d_cuda(s, Vqq, 15, 4, cfg.factor_refine_1d)
|
| 582 |
+
t_c = torch.clamp((d[:, None] * ls.to(torch.float32) * Swq - Sx) / s_Sw, min=0.0)
|
| 583 |
+
m, lm = _factor_1d_cuda(t_c, Sw, 15, 4, cfg.factor_refine_1d)
|
| 584 |
+
d = _round_fp16_t(d)
|
| 585 |
+
m = _round_fp16_t(m)
|
| 586 |
+
codes = _codes_from_params_t(X, d, m, ls, lm)
|
| 587 |
+
obj, _ = _objective_t(X, W, d, m, ls, lm, codes, None, cfg)
|
| 588 |
+
take = obj < best_obj - 1.0e-12
|
| 589 |
+
if torch.any(take):
|
| 590 |
+
best_obj = torch.where(take, obj, best_obj)
|
| 591 |
+
best_d = torch.where(take, d, best_d)
|
| 592 |
+
best_m = torch.where(take, m, best_m)
|
| 593 |
+
best_ls = torch.where(take[:, None], ls, best_ls)
|
| 594 |
+
best_lm = torch.where(take[:, None], lm, best_lm)
|
| 595 |
+
best_codes = torch.where(take[:, None], codes, best_codes)
|
| 596 |
+
|
| 597 |
+
return best_d, best_m, best_ls, best_lm, best_codes, best_obj
|
| 598 |
+
|
| 599 |
+
|
| 600 |
+
def _extended_dm_solve_and_ulp_cuda(
|
| 601 |
+
X: "torch.Tensor",
|
| 602 |
+
W: "torch.Tensor",
|
| 603 |
+
ls: "torch.Tensor",
|
| 604 |
+
lm: "torch.Tensor",
|
| 605 |
+
codes: "torch.Tensor",
|
| 606 |
+
d_cur: "torch.Tensor",
|
| 607 |
+
m_cur: "torch.Tensor",
|
| 608 |
+
carry: "torch.Tensor",
|
| 609 |
+
cfg: HPCConfig,
|
| 610 |
+
) -> tuple["torch.Tensor", "torch.Tensor"]:
|
| 611 |
+
B = X.shape[0]
|
| 612 |
+
A = ls.to(torch.float32).repeat_interleave(16, dim=1) * codes.to(torch.float32)
|
| 613 |
+
Bm = lm.to(torch.float32).repeat_interleave(16, dim=1)
|
| 614 |
+
Saa = torch.sum(W * A * A, dim=1)
|
| 615 |
+
Sab = torch.sum(W * A * Bm, dim=1)
|
| 616 |
+
Sbb = torch.sum(W * Bm * Bm, dim=1)
|
| 617 |
+
Sxa = torch.sum(W * X * A, dim=1)
|
| 618 |
+
Sxb = torch.sum(W * X * Bm, dim=1)
|
| 619 |
+
|
| 620 |
+
if cfg.use_spectral:
|
| 621 |
+
if cfg.vw_lambda > 0.0:
|
| 622 |
+
vw_w = float(cfg.vw_lambda) / QK_K
|
| 623 |
+
half = QK_K // 2
|
| 624 |
+
Ap = A[:, :half] + A[:, half:]
|
| 625 |
+
Bp = Bm[:, :half] + Bm[:, half:]
|
| 626 |
+
Xp = X[:, :half] + X[:, half:]
|
| 627 |
+
Saa = Saa + vw_w * torch.sum(Ap * Ap, dim=1)
|
| 628 |
+
Sab = Sab + vw_w * torch.sum(Ap * Bp, dim=1)
|
| 629 |
+
Sbb = Sbb + vw_w * torch.sum(Bp * Bp, dim=1)
|
| 630 |
+
Sxa = Sxa + vw_w * torch.sum(Xp * Ap, dim=1)
|
| 631 |
+
Sxb = Sxb + vw_w * torch.sum(Xp * Bp, dim=1)
|
| 632 |
+
if cfg.dc_lambda > 0.0:
|
| 633 |
+
dc_w = float(cfg.dc_lambda) / QK_K
|
| 634 |
+
rA = torch.sum(A, dim=1)
|
| 635 |
+
rB = torch.sum(Bm, dim=1)
|
| 636 |
+
rS = torch.sum(X, dim=1) + carry
|
| 637 |
+
Saa = Saa + dc_w * rA * rA
|
| 638 |
+
Sab = Sab + dc_w * rA * rB
|
| 639 |
+
Sbb = Sbb + dc_w * rB * rB
|
| 640 |
+
Sxa = Sxa + dc_w * rS * rA
|
| 641 |
+
Sxb = Sxb + dc_w * rS * rB
|
| 642 |
+
|
| 643 |
+
det = Saa * Sbb - Sab * Sab
|
| 644 |
+
valid = det > 1.0e-30
|
| 645 |
+
s_det = torch.where(valid, det, torch.ones_like(det))
|
| 646 |
+
d_new = (Sbb * Sxa - Sab * Sxb) / s_det
|
| 647 |
+
m_new = (Sab * Sxa - Saa * Sxb) / s_det
|
| 648 |
+
d_opt = torch.where(valid & (d_new > 0.0) & torch.isfinite(d_new), d_new, d_cur)
|
| 649 |
+
m_opt = torch.where(valid & (m_new >= 0.0) & torch.isfinite(m_new), m_new, m_cur)
|
| 650 |
+
|
| 651 |
+
rad = max(int(cfg.ulp_radius), 1)
|
| 652 |
+
deltas = torch.arange(-rad, rad + 1, dtype=torch.int32, device=X.device)
|
| 653 |
+
d16_c = _to_fp16_bits_t(d_opt)
|
| 654 |
+
m16_c = _to_fp16_bits_t(m_opt)
|
| 655 |
+
d_bits = torch.clamp(d16_c[:, None] + deltas[None, :], 0, 0x7BFF)
|
| 656 |
+
m_bits = torch.clamp(m16_c[:, None] + deltas[None, :], 0, 0x7BFF)
|
| 657 |
+
dd = _from_fp16_bits_t(d_bits)[:, :, None]
|
| 658 |
+
mm = _from_fp16_bits_t(m_bits)[:, None, :]
|
| 659 |
+
|
| 660 |
+
q_form = (
|
| 661 |
+
Saa[:, None, None] * (dd * dd)
|
| 662 |
+
- 2.0 * Sab[:, None, None] * (dd * mm)
|
| 663 |
+
+ Sbb[:, None, None] * (mm * mm)
|
| 664 |
+
- 2.0 * Sxa[:, None, None] * dd
|
| 665 |
+
+ 2.0 * Sxb[:, None, None] * mm
|
| 666 |
+
).reshape(B, -1)
|
| 667 |
+
best_idx = torch.argmin(q_form, dim=1)
|
| 668 |
+
K = deltas.numel()
|
| 669 |
+
rows = torch.arange(B, device=X.device)
|
| 670 |
+
return dd[rows, best_idx // K, 0], mm[rows, 0, best_idx % K]
|
| 671 |
+
|
| 672 |
+
|
| 673 |
+
def _refine_trajectory_cuda(
|
| 674 |
+
X: "torch.Tensor",
|
| 675 |
+
W: "torch.Tensor",
|
| 676 |
+
Xb: "torch.Tensor",
|
| 677 |
+
Wb: "torch.Tensor",
|
| 678 |
+
Sw: "torch.Tensor",
|
| 679 |
+
Sx: "torch.Tensor",
|
| 680 |
+
safe_Sw: "torch.Tensor",
|
| 681 |
+
d0: "torch.Tensor",
|
| 682 |
+
m0: "torch.Tensor",
|
| 683 |
+
ls0: "torch.Tensor",
|
| 684 |
+
lm0: "torch.Tensor",
|
| 685 |
+
codes0: "torch.Tensor",
|
| 686 |
+
carry: "torch.Tensor",
|
| 687 |
+
cfg: HPCConfig,
|
| 688 |
+
):
|
| 689 |
+
B = X.shape[0]
|
| 690 |
+
device = X.device
|
| 691 |
+
best_d, best_m = d0.clone(), m0.clone()
|
| 692 |
+
best_ls, best_lm = ls0.clone(), lm0.clone()
|
| 693 |
+
best_codes = codes0.clone()
|
| 694 |
+
best_obj, _ = _objective_t(X, W, best_d, best_m, best_ls, best_lm, best_codes, carry, cfg)
|
| 695 |
+
d, m, ls, lm, codes = best_d.clone(), best_m.clone(), best_ls.clone(), best_lm.clone(), best_codes.clone()
|
| 696 |
+
|
| 697 |
+
ls_deltas = torch.tensor([-3, -2, -1, 0, 1, 2, 3], dtype=torch.int64, device=device)
|
| 698 |
+
lm_deltas = torch.tensor([-1, 0, 1], dtype=torch.int64, device=device).repeat(7)
|
| 699 |
+
b_idx = torch.arange(B, device=device)[:, None]
|
| 700 |
+
s_idx = torch.arange(NS, device=device)[None, :]
|
| 701 |
+
|
| 702 |
+
for _ in range(cfg.refine_passes):
|
| 703 |
+
d_new, m_new = _extended_dm_solve_and_ulp_cuda(X, W, ls, lm, codes, d, m, carry, cfg)
|
| 704 |
+
obj_dm, _ = _objective_t(X, W, d_new, m_new, ls, lm, codes, carry, cfg)
|
| 705 |
+
imp_dm = obj_dm < best_obj - 1.0e-12
|
| 706 |
+
if torch.any(imp_dm):
|
| 707 |
+
best_obj = torch.where(imp_dm, obj_dm, best_obj)
|
| 708 |
+
best_d = torch.where(imp_dm, d_new, best_d)
|
| 709 |
+
best_m = torch.where(imp_dm, m_new, best_m)
|
| 710 |
+
best_ls = torch.where(imp_dm[:, None], ls, best_ls)
|
| 711 |
+
best_lm = torch.where(imp_dm[:, None], lm, best_lm)
|
| 712 |
+
best_codes = torch.where(imp_dm[:, None], codes, best_codes)
|
| 713 |
+
d, m = best_d.clone(), best_m.clone()
|
| 714 |
+
|
| 715 |
+
codes_re = _codes_from_params_t(X, d, m, ls, lm)
|
| 716 |
+
obj_re, _ = _objective_t(X, W, d, m, ls, lm, codes_re, carry, cfg)
|
| 717 |
+
imp_re = obj_re < best_obj - 1.0e-12
|
| 718 |
+
if torch.any(imp_re):
|
| 719 |
+
best_obj = torch.where(imp_re, obj_re, best_obj)
|
| 720 |
+
best_codes = torch.where(imp_re[:, None], codes_re, best_codes)
|
| 721 |
+
codes = best_codes.clone()
|
| 722 |
+
|
| 723 |
+
q = codes.to(torch.float32).reshape(B, NS, 16)
|
| 724 |
+
Sqq = torch.sum(Wb * q * q, dim=2)
|
| 725 |
+
Swq = torch.sum(Wb * q, dim=2)
|
| 726 |
+
Sqx = torch.sum(Wb * q * Xb, dim=2)
|
| 727 |
+
q_bar = Swq / safe_Sw
|
| 728 |
+
Vqq = torch.clamp(Sqq - Swq * q_bar, min=0.0)
|
| 729 |
+
Cqx = Sqx - Sx * q_bar
|
| 730 |
+
s_star = torch.where(
|
| 731 |
+
Vqq > 1.0e-30,
|
| 732 |
+
torch.clamp(Cqx / torch.clamp(Vqq, min=1.0e-30), min=0.0),
|
| 733 |
+
d[:, None] * ls.to(torch.float32),
|
| 734 |
+
)
|
| 735 |
+
sd = torch.where(d[:, None] > 1.0e-15, d[:, None], torch.ones((B, 1), dtype=torch.float32, device=device))
|
| 736 |
+
sm = torch.where(m[:, None] > 1.0e-15, m[:, None], torch.ones((B, 1), dtype=torch.float32, device=device))
|
| 737 |
+
ls_center = torch.where(
|
| 738 |
+
d[:, None] > 1.0e-15,
|
| 739 |
+
_nearest_int_t(s_star / sd).clamp(0, 15),
|
| 740 |
+
torch.zeros((B, NS), dtype=torch.int64, device=device),
|
| 741 |
+
)
|
| 742 |
+
c_ls_base = torch.clamp(ls_center[:, :, None] + ls_deltas[None, None, :], 0, 15).to(torch.float32)
|
| 743 |
+
d_sub_base = d[:, None, None] * c_ls_base
|
| 744 |
+
t_opt_base = torch.clamp((d_sub_base * Swq[:, :, None] - Sx[:, :, None]) / safe_Sw[:, :, None], min=0.0)
|
| 745 |
+
lm_center = torch.where(
|
| 746 |
+
m[:, None, None] > 1.0e-15,
|
| 747 |
+
_nearest_int_t(t_opt_base / sm[:, :, None]).clamp(0, 15),
|
| 748 |
+
torch.zeros_like(c_ls_base, dtype=torch.int64),
|
| 749 |
+
)
|
| 750 |
+
cand_ls = c_ls_base.repeat_interleave(3, dim=2)
|
| 751 |
+
cand_lm = torch.clamp(lm_center.repeat_interleave(3, dim=2) + lm_deltas[None, None, :], 0, 15).to(torch.float32)
|
| 752 |
+
|
| 753 |
+
d_sub_c = d[:, None, None] * cand_ls
|
| 754 |
+
m_sub_c = m[:, None, None] * cand_lm
|
| 755 |
+
safe_dc = d_sub_c >= 1.0e-15
|
| 756 |
+
sd_c = torch.where(safe_dc, d_sub_c, torch.ones_like(d_sub_c))
|
| 757 |
+
q_c = torch.where(
|
| 758 |
+
safe_dc[:, :, :, None],
|
| 759 |
+
_nearest_int_t((Xb[:, :, None, :] + m_sub_c[:, :, :, None]) / sd_c[:, :, :, None]).clamp(0, 3),
|
| 760 |
+
torch.zeros((B, NS, 21, 16), dtype=torch.int64, device=device),
|
| 761 |
+
).to(torch.float32)
|
| 762 |
+
e_c = Xb[:, :, None, :] - (d_sub_c[:, :, :, None] * q_c - m_sub_c[:, :, :, None])
|
| 763 |
+
err_c = torch.sum(Wb[:, :, None, :] * e_c * e_c, dim=3)
|
| 764 |
+
pick = torch.argmin(err_c, dim=2)
|
| 765 |
+
ls_try = cand_ls[b_idx, s_idx, pick].to(torch.uint8)
|
| 766 |
+
lm_try = cand_lm[b_idx, s_idx, pick].to(torch.uint8)
|
| 767 |
+
codes_try = _codes_from_params_t(X, d, m, ls_try, lm_try)
|
| 768 |
+
d_try, m_try = _extended_dm_solve_and_ulp_cuda(X, W, ls_try, lm_try, codes_try, d, m, carry, cfg)
|
| 769 |
+
obj_try, _ = _objective_t(X, W, d_try, m_try, ls_try, lm_try, codes_try, carry, cfg)
|
| 770 |
+
imp_sub = obj_try < best_obj - 1.0e-12
|
| 771 |
+
if torch.any(imp_sub):
|
| 772 |
+
best_obj = torch.where(imp_sub, obj_try, best_obj)
|
| 773 |
+
best_d = torch.where(imp_sub, d_try, best_d)
|
| 774 |
+
best_m = torch.where(imp_sub, m_try, best_m)
|
| 775 |
+
best_ls = torch.where(imp_sub[:, None], ls_try, best_ls)
|
| 776 |
+
best_lm = torch.where(imp_sub[:, None], lm_try, best_lm)
|
| 777 |
+
best_codes = torch.where(imp_sub[:, None], codes_try, best_codes)
|
| 778 |
+
d, m, ls, lm, codes = best_d.clone(), best_m.clone(), best_ls.clone(), best_lm.clone(), best_codes.clone()
|
| 779 |
+
|
| 780 |
+
return best_d, best_m, best_ls, best_lm, best_codes, best_obj
|
| 781 |
+
|
| 782 |
+
|
| 783 |
+
def _compute_dc_carry_fast(
|
| 784 |
+
X_np: np.ndarray,
|
| 785 |
+
scales_seed: np.ndarray,
|
| 786 |
+
mins_seed: np.ndarray,
|
| 787 |
+
best: np.ndarray,
|
| 788 |
+
base_d: np.ndarray,
|
| 789 |
+
base_m: np.ndarray,
|
| 790 |
+
cfg: HPCConfig,
|
| 791 |
+
row_width: int | None,
|
| 792 |
+
) -> np.ndarray:
|
| 793 |
+
"""Vectorized residual DC computation + fast row recurrence."""
|
| 794 |
+
n = X_np.shape[0]
|
| 795 |
+
di = best // N_CAND_M
|
| 796 |
+
mi = best % N_CAND_M
|
| 797 |
+
d = (base_d * MULTS_D[di]).astype(np.float16).astype(np.float32)
|
| 798 |
+
m = (base_m * MULTS_M[mi]).astype(np.float16).astype(np.float32)
|
| 799 |
+
sd = np.where(d[:, None] > 1.0e-15, d[:, None], 1.0)
|
| 800 |
+
sm = np.where(m[:, None] > 1.0e-15, m[:, None], 1.0)
|
| 801 |
+
ls = np.where(d[:, None] > 1.0e-15, np.clip(np.rint(scales_seed / sd), 0, 15), 0).astype(np.float32)
|
| 802 |
+
lm = np.where(m[:, None] > 1.0e-15, np.clip(np.rint(mins_seed / sm), 0, 15), 0).astype(np.float32)
|
| 803 |
+
d_sub = np.repeat(d[:, None] * ls, 16, axis=1)
|
| 804 |
+
m_sub = np.repeat(m[:, None] * lm, 16, axis=1)
|
| 805 |
+
safe = d_sub >= 1.0e-15
|
| 806 |
+
q = np.where(safe, np.clip(np.rint((X_np + m_sub) / np.where(safe, d_sub, 1.0)), 0, 3), 0.0)
|
| 807 |
+
dc_res = np.sum(X_np - (d_sub * q - m_sub), axis=1, dtype=np.float64)
|
| 808 |
+
|
| 809 |
+
bpr = row_width // QK_K if row_width and row_width % QK_K == 0 else 0
|
| 810 |
+
carry = np.zeros(n, dtype=np.float32)
|
| 811 |
+
decay = float(cfg.dc_decay)
|
| 812 |
+
cum = bool(cfg.carry_cumulative)
|
| 813 |
+
if bpr > 0 and n % bpr == 0:
|
| 814 |
+
n_rows = n // bpr
|
| 815 |
+
res_2d = dc_res.reshape(n_rows, bpr)
|
| 816 |
+
car_2d = np.zeros((n_rows, bpr), dtype=np.float64)
|
| 817 |
+
rolling = np.zeros(n_rows, dtype=np.float64)
|
| 818 |
+
for col in range(bpr):
|
| 819 |
+
car_2d[:, col] = decay * rolling
|
| 820 |
+
rolling = (car_2d[:, col] + res_2d[:, col]) if cum else res_2d[:, col]
|
| 821 |
+
carry = car_2d.reshape(n).astype(np.float32)
|
| 822 |
+
else:
|
| 823 |
+
rolling_s = 0.0
|
| 824 |
+
for i in range(n):
|
| 825 |
+
if bpr > 0 and i % bpr == 0:
|
| 826 |
+
rolling_s = 0.0
|
| 827 |
+
carry[i] = np.float32(decay * rolling_s)
|
| 828 |
+
rolling_s = (carry[i] + dc_res[i]) if cum else dc_res[i]
|
| 829 |
+
return carry
|
| 830 |
+
|
| 831 |
+
|
| 832 |
+
def _apply_dc_nudge_cuda(
|
| 833 |
+
X_np: np.ndarray,
|
| 834 |
+
W_np: np.ndarray | None,
|
| 835 |
+
final_d: np.ndarray,
|
| 836 |
+
final_m: np.ndarray,
|
| 837 |
+
final_ls: np.ndarray,
|
| 838 |
+
final_lm: np.ndarray,
|
| 839 |
+
final_codes: np.ndarray,
|
| 840 |
+
cfg: HPCConfig,
|
| 841 |
+
row_width: int | None,
|
| 842 |
+
dev: "torch.device",
|
| 843 |
+
) -> np.ndarray:
|
| 844 |
+
"""Row-parallel GPU DC residual nudge across all rows simultaneously."""
|
| 845 |
+
n = X_np.shape[0]
|
| 846 |
+
bpr = row_width // QK_K if row_width and row_width % QK_K == 0 else 0
|
| 847 |
+
if bpr <= 0 or n % bpr != 0:
|
| 848 |
+
return final_codes
|
| 849 |
+
|
| 850 |
+
n_rows = n // bpr
|
| 851 |
+
codes_out = final_codes.copy().reshape(n_rows, bpr, QK_K)
|
| 852 |
+
X_r = X_np.reshape(n_rows, bpr, QK_K)
|
| 853 |
+
W_r = None if W_np is None else W_np.reshape(n_rows, bpr, QK_K)
|
| 854 |
+
d_r = final_d.reshape(n_rows, bpr)
|
| 855 |
+
m_r = final_m.reshape(n_rows, bpr)
|
| 856 |
+
ls_r = final_ls.reshape(n_rows, bpr, NS)
|
| 857 |
+
lm_r = final_lm.reshape(n_rows, bpr, NS)
|
| 858 |
+
|
| 859 |
+
decay = float(cfg.dc_decay)
|
| 860 |
+
cum = bool(cfg.carry_cumulative)
|
| 861 |
+
row_batch = 1024
|
| 862 |
+
q_opts = torch.arange(4, dtype=torch.float32, device=dev)[None, :, None] # (1, 4, 1)
|
| 863 |
+
|
| 864 |
+
for r0 in range(0, n_rows, row_batch):
|
| 865 |
+
r1 = min(r0 + row_batch, n_rows)
|
| 866 |
+
R = r1 - r0
|
| 867 |
+
rolling = torch.zeros(R, dtype=torch.float32, device=dev)
|
| 868 |
+
X_batch = torch.from_numpy(X_r[r0:r1]).to(dev)
|
| 869 |
+
W_batch = (
|
| 870 |
+
torch.ones_like(X_batch)
|
| 871 |
+
if W_r is None
|
| 872 |
+
else torch.from_numpy(W_r[r0:r1]).to(dev)
|
| 873 |
+
)
|
| 874 |
+
d_batch = torch.from_numpy(d_r[r0:r1]).to(dev)
|
| 875 |
+
m_batch = torch.from_numpy(m_r[r0:r1]).to(dev)
|
| 876 |
+
ls_batch = torch.from_numpy(ls_r[r0:r1]).to(dev, dtype=torch.float32)
|
| 877 |
+
lm_batch = torch.from_numpy(lm_r[r0:r1]).to(dev, dtype=torch.float32)
|
| 878 |
+
cd_batch = torch.from_numpy(codes_out[r0:r1]).to(dev, dtype=torch.int64)
|
| 879 |
+
rows = torch.arange(R, device=dev)
|
| 880 |
+
|
| 881 |
+
for col in range(bpr):
|
| 882 |
+
carry = decay * rolling
|
| 883 |
+
x_c = X_batch[:, col]
|
| 884 |
+
w_c = W_batch[:, col]
|
| 885 |
+
d_c = d_batch[:, col]
|
| 886 |
+
m_c = m_batch[:, col]
|
| 887 |
+
ls_c = ls_batch[:, col]
|
| 888 |
+
lm_c = lm_batch[:, col]
|
| 889 |
+
cd_c = cd_batch[:, col]
|
| 890 |
+
|
| 891 |
+
d_sub = (d_c[:, None] * ls_c).repeat_interleave(16, dim=1)
|
| 892 |
+
m_sub = (m_c[:, None] * lm_c).repeat_interleave(16, dim=1)
|
| 893 |
+
active = d_sub >= 1.0e-15
|
| 894 |
+
e = x_c - (d_sub * cd_c.to(torch.float32) - m_sub)
|
| 895 |
+
sse = torch.sum(w_c * e * e, dim=1)
|
| 896 |
+
cap = sse * (1.0 + float(cfg.sse_budget))
|
| 897 |
+
res = torch.sum(e, dim=1) + carry
|
| 898 |
+
|
| 899 |
+
for _ in range(2):
|
| 900 |
+
e_new = x_c[:, None, :] - (d_sub[:, None, :] * q_opts - m_sub[:, None, :])
|
| 901 |
+
dsse = w_c[:, None, :] * (e_new * e_new - e[:, None, :] * e[:, None, :])
|
| 902 |
+
mask = (
|
| 903 |
+
active[:, None, :]
|
| 904 |
+
& (q_opts.to(torch.int64) != cd_c[:, None, :])
|
| 905 |
+
& ((sse[:, None, None] + dsse) <= cap[:, None, None])
|
| 906 |
+
)
|
| 907 |
+
new_res = res[:, None, None] + (e_new - e[:, None, :])
|
| 908 |
+
impr = torch.where(
|
| 909 |
+
mask,
|
| 910 |
+
torch.abs(res)[:, None, None] - torch.abs(new_res),
|
| 911 |
+
torch.full_like(new_res, -float("inf")),
|
| 912 |
+
).reshape(R, -1)
|
| 913 |
+
best_imp, flat_idx = torch.max(impr, dim=1)
|
| 914 |
+
do_upd = best_imp > 1.0e-12
|
| 915 |
+
if not torch.any(do_upd):
|
| 916 |
+
break
|
| 917 |
+
q_pick = flat_idx // QK_K
|
| 918 |
+
k_pick = flat_idx % QK_K
|
| 919 |
+
old_e = e[rows, k_pick]
|
| 920 |
+
chosen_e = e_new[rows, q_pick, k_pick]
|
| 921 |
+
de = chosen_e - old_e
|
| 922 |
+
res = torch.where(do_upd, res + de, res)
|
| 923 |
+
sse = torch.where(
|
| 924 |
+
do_upd,
|
| 925 |
+
sse + w_c[rows, k_pick] * (chosen_e * chosen_e - old_e * old_e),
|
| 926 |
+
sse,
|
| 927 |
+
)
|
| 928 |
+
e[rows, k_pick] = torch.where(do_upd, chosen_e, old_e)
|
| 929 |
+
cd_c[rows, k_pick] = torch.where(do_upd, q_pick, cd_c[rows, k_pick])
|
| 930 |
+
|
| 931 |
+
cd_batch[:, col] = cd_c
|
| 932 |
+
dc_res = torch.sum(e, dim=1)
|
| 933 |
+
rolling = (carry + dc_res) if cum else dc_res
|
| 934 |
+
|
| 935 |
+
codes_out[r0:r1] = cd_batch.cpu().numpy().astype(np.uint8)
|
| 936 |
+
|
| 937 |
+
return codes_out.reshape(n, QK_K)
|
| 938 |
+
|
| 939 |
+
|
| 940 |
+
def quantize_cuda(
|
| 941 |
+
weights: np.ndarray,
|
| 942 |
+
importance: np.ndarray | None,
|
| 943 |
+
row_width: int | None,
|
| 944 |
+
cfg: HPCConfig,
|
| 945 |
+
status_callback=None,
|
| 946 |
+
stop_event=None,
|
| 947 |
+
device: str = "cuda",
|
| 948 |
+
) -> QuantizedTensor:
|
| 949 |
+
"""Run the full HPC-Quantize V2 pipeline on a CUDA GPU."""
|
| 950 |
+
if not cuda_is_available():
|
| 951 |
+
raise RuntimeError("PyTorch CUDA is not available on this system")
|
| 952 |
+
|
| 953 |
+
def _emit(phase: str, done: int, total: int, detail: str, **extra) -> None:
|
| 954 |
+
if status_callback is not None:
|
| 955 |
+
status_callback({"phase": phase, "done": done, "total": total, "detail": detail, **extra})
|
| 956 |
+
|
| 957 |
+
def _check_stop() -> None:
|
| 958 |
+
if stop_event is not None and stop_event.is_set():
|
| 959 |
+
raise InterruptedError("Quantization cancelled by user")
|
| 960 |
+
|
| 961 |
+
x = np.asarray(weights, dtype=np.float32)
|
| 962 |
+
if x.size == 0:
|
| 963 |
+
raise ValueError("weight tensor is empty")
|
| 964 |
+
if x.size % QK_K != 0:
|
| 965 |
+
raise ValueError("weight count must be a multiple of 256")
|
| 966 |
+
if x.ndim == 2 and row_width is None and x.shape[1] % QK_K == 0:
|
| 967 |
+
row_width = int(x.shape[1])
|
| 968 |
+
x = x.ravel()
|
| 969 |
+
n_blocks = x.size // QK_K
|
| 970 |
+
X_np = x.reshape(n_blocks, QK_K)
|
| 971 |
+
|
| 972 |
+
if importance is not None:
|
| 973 |
+
imp = np.asarray(importance, dtype=np.float32).ravel()
|
| 974 |
+
if imp.size != x.size:
|
| 975 |
+
raise ValueError("importance must match the weight count")
|
| 976 |
+
if np.any(imp < 0.0) or not np.all(np.isfinite(imp)):
|
| 977 |
+
raise ValueError("importance must be finite and non-negative")
|
| 978 |
+
W_np = imp.reshape(n_blocks, QK_K)
|
| 979 |
+
else:
|
| 980 |
+
W_np = None
|
| 981 |
+
|
| 982 |
+
sigma = float(np.sqrt(np.mean(x.astype(np.float64) ** 2)))
|
| 983 |
+
clamp_val = seed_clamp_value(x, cfg)
|
| 984 |
+
dev = torch.device(device)
|
| 985 |
+
gpu_name = cuda_device_name(device)
|
| 986 |
+
batch_blocks = 4096
|
| 987 |
+
|
| 988 |
+
candidate_errors = np.empty((n_blocks, TOTAL_CANDIDATES), dtype=np.float32)
|
| 989 |
+
scales_seed = np.empty((n_blocks, NS), dtype=np.float32)
|
| 990 |
+
mins_seed = np.empty((n_blocks, NS), dtype=np.float32)
|
| 991 |
+
base_d = np.empty((n_blocks,), dtype=np.float32)
|
| 992 |
+
base_m = np.empty((n_blocks,), dtype=np.float32)
|
| 993 |
+
solo_d = np.empty((n_blocks,), dtype=np.float32)
|
| 994 |
+
solo_m = np.empty((n_blocks,), dtype=np.float32)
|
| 995 |
+
solo_ls = np.empty((n_blocks, NS), dtype=np.uint8)
|
| 996 |
+
solo_lm = np.empty((n_blocks, NS), dtype=np.uint8)
|
| 997 |
+
solo_codes = np.empty((n_blocks, QK_K), dtype=np.uint8)
|
| 998 |
+
solo_obj = np.empty((n_blocks,), dtype=np.float64)
|
| 999 |
+
|
| 1000 |
+
t0 = time.time()
|
| 1001 |
+
with torch.no_grad():
|
| 1002 |
+
for start in range(0, n_blocks, batch_blocks):
|
| 1003 |
+
_check_stop()
|
| 1004 |
+
stop = min(start + batch_blocks, n_blocks)
|
| 1005 |
+
X_t = torch.from_numpy(X_np[start:stop]).to(dev)
|
| 1006 |
+
W_t = (
|
| 1007 |
+
torch.ones_like(X_t)
|
| 1008 |
+
if W_np is None
|
| 1009 |
+
else torch.from_numpy(W_np[start:stop]).to(dev)
|
| 1010 |
+
)
|
| 1011 |
+
seeds_t = _build_seeds_cuda(X_t, None if W_np is None else W_t, clamp_val)
|
| 1012 |
+
bd_t, bm_t = _phase2_wls_cuda(X_t, W_t, seeds_t, clamp_val)
|
| 1013 |
+
|
| 1014 |
+
scales_seed[start:stop] = seeds_t["scales"].cpu().numpy()
|
| 1015 |
+
mins_seed[start:stop] = seeds_t["mins"].cpu().numpy()
|
| 1016 |
+
base_d[start:stop] = bd_t.cpu().numpy()
|
| 1017 |
+
base_m[start:stop] = bm_t.cpu().numpy()
|
| 1018 |
+
|
| 1019 |
+
candidate_errors[start:stop] = _factor_candidates_cuda(
|
| 1020 |
+
X_t, W_t, seeds_t["scales"], seeds_t["mins"], bd_t, bm_t, cfg
|
| 1021 |
+
)
|
| 1022 |
+
sd_t, sm_t, sls_t, slm_t, sc_t, sobj_t = _standalone_coupled_solve_cuda(X_t, W_t, cfg)
|
| 1023 |
+
solo_d[start:stop] = sd_t.cpu().numpy()
|
| 1024 |
+
solo_m[start:stop] = sm_t.cpu().numpy()
|
| 1025 |
+
solo_ls[start:stop] = sls_t.cpu().numpy()
|
| 1026 |
+
solo_lm[start:stop] = slm_t.cpu().numpy()
|
| 1027 |
+
solo_codes[start:stop] = sc_t.cpu().numpy()
|
| 1028 |
+
solo_obj[start:stop] = sobj_t.cpu().numpy()
|
| 1029 |
+
|
| 1030 |
+
elapsed = max(time.time() - t0, 1.0e-6)
|
| 1031 |
+
rate = stop / elapsed
|
| 1032 |
+
eta = (n_blocks - stop) / max(rate, 1.0e-9)
|
| 1033 |
+
_emit(
|
| 1034 |
+
f"CUDA Phase 1–2b ({gpu_name}): Seeds + Analogs + Factored Field",
|
| 1035 |
+
stop,
|
| 1036 |
+
n_blocks,
|
| 1037 |
+
f"blk {stop}/{n_blocks} ({rate:.1f} blk/s, ETA {eta:.1f}s) "
|
| 1038 |
+
f"mean_obj={float(np.mean(solo_obj[:stop])):.5e}",
|
| 1039 |
+
rate=rate,
|
| 1040 |
+
eta=eta,
|
| 1041 |
+
sigma=sigma,
|
| 1042 |
+
)
|
| 1043 |
+
|
| 1044 |
+
_check_stop()
|
| 1045 |
+
_emit(
|
| 1046 |
+
"Phase 3: 6-State Sieve & 36-State Viterbi DP",
|
| 1047 |
+
0,
|
| 1048 |
+
n_blocks,
|
| 1049 |
+
f"Running Sieve + 36-state Viterbi sequence optimization ({cfg.optimizer.upper()})",
|
| 1050 |
+
sigma=sigma,
|
| 1051 |
+
)
|
| 1052 |
+
best, temperature, _ = _phase3_select(candidate_errors, cfg)
|
| 1053 |
+
carry = _compute_dc_carry_fast(
|
| 1054 |
+
X_np, scales_seed, mins_seed, best, base_d, base_m, cfg, row_width
|
| 1055 |
+
)
|
| 1056 |
+
|
| 1057 |
+
final_d = np.empty((n_blocks,), dtype=np.float32)
|
| 1058 |
+
final_m = np.empty((n_blocks,), dtype=np.float32)
|
| 1059 |
+
final_ls = np.empty((n_blocks, NS), dtype=np.uint8)
|
| 1060 |
+
final_lm = np.empty((n_blocks, NS), dtype=np.uint8)
|
| 1061 |
+
final_codes = np.empty((n_blocks, QK_K), dtype=np.uint8)
|
| 1062 |
+
factor_floor_used = 0
|
| 1063 |
+
t_p4 = time.time()
|
| 1064 |
+
|
| 1065 |
+
for start in range(0, n_blocks, batch_blocks):
|
| 1066 |
+
_check_stop()
|
| 1067 |
+
stop = min(start + batch_blocks, n_blocks)
|
| 1068 |
+
B_c = stop - start
|
| 1069 |
+
X_t = torch.from_numpy(X_np[start:stop]).to(dev)
|
| 1070 |
+
W_t = (
|
| 1071 |
+
torch.ones_like(X_t)
|
| 1072 |
+
if W_np is None
|
| 1073 |
+
else torch.from_numpy(W_np[start:stop]).to(dev)
|
| 1074 |
+
)
|
| 1075 |
+
Xb = X_t.reshape(B_c, NS, 16)
|
| 1076 |
+
Wb = W_t.reshape(B_c, NS, 16)
|
| 1077 |
+
Sw = torch.sum(Wb, dim=2)
|
| 1078 |
+
Sx = torch.sum(Wb * Xb, dim=2)
|
| 1079 |
+
safe_Sw = torch.clamp(Sw, min=1.0e-30)
|
| 1080 |
+
carry_t = torch.from_numpy(carry[start:stop]).to(dev)
|
| 1081 |
+
|
| 1082 |
+
b_slice = best[start:stop]
|
| 1083 |
+
di = b_slice // N_CAND_M
|
| 1084 |
+
mi = b_slice % N_CAND_M
|
| 1085 |
+
d_v = torch.from_numpy(base_d[start:stop] * MULTS_D[di]).to(dev)
|
| 1086 |
+
m_v = torch.from_numpy(base_m[start:stop] * MULTS_M[mi]).to(dev)
|
| 1087 |
+
d_v = _round_fp16_t(d_v)
|
| 1088 |
+
m_v = _round_fp16_t(m_v)
|
| 1089 |
+
sc_s = torch.from_numpy(scales_seed[start:stop]).to(dev)
|
| 1090 |
+
mn_s = torch.from_numpy(mins_seed[start:stop]).to(dev)
|
| 1091 |
+
sd_v = torch.where(d_v[:, None] > 1.0e-15, d_v[:, None], torch.ones_like(d_v[:, None]))
|
| 1092 |
+
sm_v = torch.where(m_v[:, None] > 1.0e-15, m_v[:, None], torch.ones_like(m_v[:, None]))
|
| 1093 |
+
ls_v = torch.where(
|
| 1094 |
+
d_v[:, None] > 1.0e-15,
|
| 1095 |
+
_nearest_int_t(sc_s / sd_v).clamp(0, 15),
|
| 1096 |
+
torch.zeros((B_c, NS), dtype=torch.int64, device=dev),
|
| 1097 |
+
).to(torch.uint8)
|
| 1098 |
+
lm_v = torch.where(
|
| 1099 |
+
m_v[:, None] > 1.0e-15,
|
| 1100 |
+
_nearest_int_t(mn_s / sm_v).clamp(0, 15),
|
| 1101 |
+
torch.zeros((B_c, NS), dtype=torch.int64, device=dev),
|
| 1102 |
+
).to(torch.uint8)
|
| 1103 |
+
codes_v = _codes_from_params_t(X_t, d_v, m_v, ls_v, lm_v)
|
| 1104 |
+
|
| 1105 |
+
d1, m1, ls1, lm1, c1, obj1 = _refine_trajectory_cuda(
|
| 1106 |
+
X_t, W_t, Xb, Wb, Sw, Sx, safe_Sw, d_v, m_v, ls_v, lm_v, codes_v, carry_t, cfg
|
| 1107 |
+
)
|
| 1108 |
+
|
| 1109 |
+
if cfg.use_factor_floor:
|
| 1110 |
+
d_f = torch.from_numpy(solo_d[start:stop]).to(dev)
|
| 1111 |
+
m_f = torch.from_numpy(solo_m[start:stop]).to(dev)
|
| 1112 |
+
ls_f = torch.from_numpy(solo_ls[start:stop]).to(dev)
|
| 1113 |
+
lm_f = torch.from_numpy(solo_lm[start:stop]).to(dev)
|
| 1114 |
+
c_f = torch.from_numpy(solo_codes[start:stop]).to(dev)
|
| 1115 |
+
d2, m2, ls2, lm2, c2, obj2 = _refine_trajectory_cuda(
|
| 1116 |
+
X_t, W_t, Xb, Wb, Sw, Sx, safe_Sw, d_f, m_f, ls_f, lm_f, c_f, carry_t, cfg
|
| 1117 |
+
)
|
| 1118 |
+
take2 = obj2 < obj1 - 1.0e-12
|
| 1119 |
+
factor_floor_used += int(torch.sum(take2).item())
|
| 1120 |
+
d1 = torch.where(take2, d2, d1)
|
| 1121 |
+
m1 = torch.where(take2, m2, m1)
|
| 1122 |
+
ls1 = torch.where(take2[:, None], ls2, ls1)
|
| 1123 |
+
lm1 = torch.where(take2[:, None], lm2, lm1)
|
| 1124 |
+
c1 = torch.where(take2[:, None], c2, c1)
|
| 1125 |
+
|
| 1126 |
+
final_d[start:stop] = d1.cpu().numpy()
|
| 1127 |
+
final_m[start:stop] = m1.cpu().numpy()
|
| 1128 |
+
final_ls[start:stop] = ls1.cpu().numpy()
|
| 1129 |
+
final_lm[start:stop] = lm1.cpu().numpy()
|
| 1130 |
+
final_codes[start:stop] = c1.cpu().numpy()
|
| 1131 |
+
|
| 1132 |
+
elapsed = max(time.time() - t_p4, 1.0e-6)
|
| 1133 |
+
rate = stop / elapsed
|
| 1134 |
+
eta = (n_blocks - stop) / max(rate, 1.0e-9)
|
| 1135 |
+
_emit(
|
| 1136 |
+
f"CUDA Phase 4 ({gpu_name}): Algebraic Coupled Bilinear Assembly",
|
| 1137 |
+
stop,
|
| 1138 |
+
n_blocks,
|
| 1139 |
+
f"blk {stop}/{n_blocks} ({rate:.1f} blk/s, ETA {eta:.1f}s) floors(fac={factor_floor_used})",
|
| 1140 |
+
rate=rate,
|
| 1141 |
+
eta=eta,
|
| 1142 |
+
sigma=sigma,
|
| 1143 |
+
temperature=temperature,
|
| 1144 |
+
cand_floors=0,
|
| 1145 |
+
fac_floors=factor_floor_used,
|
| 1146 |
+
)
|
| 1147 |
+
|
| 1148 |
+
if cfg.apply_dc_carry and (cfg.dc_lambda > 0.0 or cfg.dc_decay > 0.0):
|
| 1149 |
+
_check_stop()
|
| 1150 |
+
final_codes = _apply_dc_nudge_cuda(
|
| 1151 |
+
X_np, W_np, final_d, final_m, final_ls, final_lm, final_codes, cfg, row_width, dev
|
| 1152 |
+
)
|
| 1153 |
+
|
| 1154 |
+
scales = (final_ls & 0x0F) | ((final_lm & 0x0F) << np.uint8(4))
|
| 1155 |
+
qs = np.empty((n_blocks, QK_K // 4), dtype=np.uint8)
|
| 1156 |
+
for base in (0, 128):
|
| 1157 |
+
qs[:, base // 4 : base // 4 + 32] = (
|
| 1158 |
+
final_codes[:, base : base + 32]
|
| 1159 |
+
| (final_codes[:, base + 32 : base + 64] << np.uint8(2))
|
| 1160 |
+
| (final_codes[:, base + 64 : base + 96] << np.uint8(4))
|
| 1161 |
+
| (final_codes[:, base + 96 : base + 128] << np.uint8(6))
|
| 1162 |
+
)
|
| 1163 |
+
dm = np.empty((n_blocks, 2), dtype=np.uint16)
|
| 1164 |
+
dm[:, 0] = final_d.astype(np.float16).view(np.uint16)
|
| 1165 |
+
dm[:, 1] = final_m.astype(np.float16).view(np.uint16)
|
| 1166 |
+
|
| 1167 |
+
d_f32 = dm[:, 0].view(np.float16).astype(np.float32)
|
| 1168 |
+
m_f32 = dm[:, 1].view(np.float16).astype(np.float32)
|
| 1169 |
+
d_sub = np.repeat(d_f32[:, None] * final_ls.astype(np.float32), 16, axis=1)
|
| 1170 |
+
m_sub = np.repeat(m_f32[:, None] * final_lm.astype(np.float32), 16, axis=1)
|
| 1171 |
+
xh_all = d_sub * final_codes.astype(np.float32) - m_sub
|
| 1172 |
+
err_mat = X_np.astype(np.float64) - xh_all.astype(np.float64)
|
| 1173 |
+
total_sse = float(np.sum(err_mat * err_mat))
|
| 1174 |
+
weighted = total_sse if W_np is None else float(np.sum(W_np.astype(np.float64) * err_mat * err_mat))
|
| 1175 |
+
rmse = float(np.sqrt(total_sse / x.size))
|
| 1176 |
+
|
| 1177 |
+
blocks = [
|
| 1178 |
+
Q2KBlock(scales=scales[i], qs=qs[i], d=np.uint16(dm[i, 0]), dmin=np.uint16(dm[i, 1]))
|
| 1179 |
+
for i in range(n_blocks)
|
| 1180 |
+
]
|
| 1181 |
+
|
| 1182 |
+
stats = TensorStats(
|
| 1183 |
+
n_blocks=n_blocks,
|
| 1184 |
+
total_sse=total_sse,
|
| 1185 |
+
weighted_sse=weighted,
|
| 1186 |
+
rmse=rmse,
|
| 1187 |
+
sigma=sigma,
|
| 1188 |
+
rmse_over_sigma=rmse / sigma if sigma > 1.0e-15 else 0.0,
|
| 1189 |
+
temperature=temperature,
|
| 1190 |
+
factor_floor_used=factor_floor_used,
|
| 1191 |
+
candidate_floor_used=0,
|
| 1192 |
+
)
|
| 1193 |
+
_emit(
|
| 1194 |
+
f"Phase 5: Tensor Complete ({gpu_name})",
|
| 1195 |
+
n_blocks,
|
| 1196 |
+
n_blocks,
|
| 1197 |
+
f"Done: blocks={n_blocks} rmse={rmse:.6e} rmse/sigma={stats.rmse_over_sigma:.5f} "
|
| 1198 |
+
f"wSSE={weighted:.5e} temp={temperature:.3e} floors(fac={factor_floor_used})",
|
| 1199 |
+
rmse=rmse,
|
| 1200 |
+
sigma=sigma,
|
| 1201 |
+
temperature=temperature,
|
| 1202 |
+
cand_floors=0,
|
| 1203 |
+
fac_floors=factor_floor_used,
|
| 1204 |
+
)
|
| 1205 |
+
return QuantizedTensor(
|
| 1206 |
+
blocks=blocks,
|
| 1207 |
+
scales=scales,
|
| 1208 |
+
qs=qs,
|
| 1209 |
+
dm=dm,
|
| 1210 |
+
best_candidate=best,
|
| 1211 |
+
factor_objectives=solo_obj,
|
| 1212 |
+
config=cfg,
|
| 1213 |
+
stats=stats,
|
| 1214 |
+
)
|
gui.py
CHANGED
|
@@ -34,6 +34,8 @@ from hpc_quantize_v2 import (
|
|
| 34 |
GGUFReader,
|
| 35 |
HPCConfig,
|
| 36 |
Q2KQuantizer,
|
|
|
|
|
|
|
| 37 |
expand_importance,
|
| 38 |
factor_only_solve,
|
| 39 |
lookup_imatrix,
|
|
@@ -98,6 +100,7 @@ class HPCQuantizeGUI:
|
|
| 98 |
self.ulp_var = tk.IntVar(value=4)
|
| 99 |
self.limit_var = tk.IntVar(value=0)
|
| 100 |
self.workers_var = tk.IntVar(value=0)
|
|
|
|
| 101 |
|
| 102 |
self.use_analogs_var = tk.BooleanVar(value=True)
|
| 103 |
self.coupled_q2k_var = tk.BooleanVar(value=True)
|
|
@@ -138,6 +141,7 @@ class HPCQuantizeGUI:
|
|
| 138 |
self.cand_m_var,
|
| 139 |
self.limit_var,
|
| 140 |
self.workers_var,
|
|
|
|
| 141 |
self.use_spectral_var,
|
| 142 |
):
|
| 143 |
v.trace_add("write", lambda *_: self._update_cli_preview())
|
|
@@ -274,6 +278,11 @@ class HPCQuantizeGUI:
|
|
| 274 |
self._spin_pair(grid_f, 2, 0, "FP16 ULP Radius:", self.ulp_var, 0, 16)
|
| 275 |
self._spin_pair(grid_f, 2, 2, "Tensor Limit (0=all):", self.limit_var, 0, 9999)
|
| 276 |
self._spin_pair(grid_f, 3, 0, "Workers (0=Auto CPU):", self.workers_var, 0, 256)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 277 |
|
| 278 |
spec_f = ttk.LabelFrame(opt_frame, text="Spectral & DC Parameters", padding=6)
|
| 279 |
spec_f.pack(fill=tk.X, pady=2)
|
|
@@ -421,6 +430,10 @@ class HPCQuantizeGUI:
|
|
| 421 |
sp.grid(row=row, column=col + 1, sticky=tk.EW, padx=4, pady=2)
|
| 422 |
|
| 423 |
def _auto_populate_defaults(self) -> None:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 424 |
default_gguf = Path("Raon.gguf")
|
| 425 |
default_imat = Path("imatrix.gguf")
|
| 426 |
if default_imat.exists():
|
|
@@ -484,6 +497,7 @@ class HPCQuantizeGUI:
|
|
| 484 |
vw_lambda=float(self.vw_lambda_var.get()),
|
| 485 |
dc_decay=float(self.dc_decay_var.get()),
|
| 486 |
sse_budget=float(self.sse_budget_var.get()),
|
|
|
|
| 487 |
workers=int(self.workers_var.get()),
|
| 488 |
verbose=False,
|
| 489 |
)
|
|
@@ -507,6 +521,7 @@ class HPCQuantizeGUI:
|
|
| 507 |
cmd += ["--output", out]
|
| 508 |
if self.limit_var.get() > 0:
|
| 509 |
cmd += ["--limit", str(self.limit_var.get())]
|
|
|
|
| 510 |
cmd += ["--workers", str(self.workers_var.get())]
|
| 511 |
cmd += ["--cand-d", str(self.cand_d_var.get()), "--cand-m", str(self.cand_m_var.get())]
|
| 512 |
if self.optimizer_var.get() == "mse":
|
|
|
|
| 34 |
GGUFReader,
|
| 35 |
HPCConfig,
|
| 36 |
Q2KQuantizer,
|
| 37 |
+
cuda_device_name,
|
| 38 |
+
cuda_is_available,
|
| 39 |
expand_importance,
|
| 40 |
factor_only_solve,
|
| 41 |
lookup_imatrix,
|
|
|
|
| 100 |
self.ulp_var = tk.IntVar(value=4)
|
| 101 |
self.limit_var = tk.IntVar(value=0)
|
| 102 |
self.workers_var = tk.IntVar(value=0)
|
| 103 |
+
self.device_var = tk.StringVar(value="auto")
|
| 104 |
|
| 105 |
self.use_analogs_var = tk.BooleanVar(value=True)
|
| 106 |
self.coupled_q2k_var = tk.BooleanVar(value=True)
|
|
|
|
| 141 |
self.cand_m_var,
|
| 142 |
self.limit_var,
|
| 143 |
self.workers_var,
|
| 144 |
+
self.device_var,
|
| 145 |
self.use_spectral_var,
|
| 146 |
):
|
| 147 |
v.trace_add("write", lambda *_: self._update_cli_preview())
|
|
|
|
| 278 |
self._spin_pair(grid_f, 2, 0, "FP16 ULP Radius:", self.ulp_var, 0, 16)
|
| 279 |
self._spin_pair(grid_f, 2, 2, "Tensor Limit (0=all):", self.limit_var, 0, 9999)
|
| 280 |
self._spin_pair(grid_f, 3, 0, "Workers (0=Auto CPU):", self.workers_var, 0, 256)
|
| 281 |
+
ttk.Label(grid_f, text="Compute Device:").grid(row=3, column=2, sticky=tk.W, padx=4, pady=2)
|
| 282 |
+
dev_cb = ttk.Combobox(
|
| 283 |
+
grid_f, textvariable=self.device_var, values=("auto", "cuda", "cpu"), state="readonly", width=8
|
| 284 |
+
)
|
| 285 |
+
dev_cb.grid(row=3, column=3, sticky=tk.EW, padx=4, pady=2)
|
| 286 |
|
| 287 |
spec_f = ttk.LabelFrame(opt_frame, text="Spectral & DC Parameters", padding=6)
|
| 288 |
spec_f.pack(fill=tk.X, pady=2)
|
|
|
|
| 430 |
sp.grid(row=row, column=col + 1, sticky=tk.EW, padx=4, pady=2)
|
| 431 |
|
| 432 |
def _auto_populate_defaults(self) -> None:
|
| 433 |
+
if cuda_is_available():
|
| 434 |
+
self._log(f"CUDA GPU detected: {cuda_device_name()} (device=auto will use GPU)")
|
| 435 |
+
else:
|
| 436 |
+
self._log("CUDA GPU not detected (using multi-core CPU backend)")
|
| 437 |
default_gguf = Path("Raon.gguf")
|
| 438 |
default_imat = Path("imatrix.gguf")
|
| 439 |
if default_imat.exists():
|
|
|
|
| 497 |
vw_lambda=float(self.vw_lambda_var.get()),
|
| 498 |
dc_decay=float(self.dc_decay_var.get()),
|
| 499 |
sse_budget=float(self.sse_budget_var.get()),
|
| 500 |
+
device=self.device_var.get(),
|
| 501 |
workers=int(self.workers_var.get()),
|
| 502 |
verbose=False,
|
| 503 |
)
|
|
|
|
| 521 |
cmd += ["--output", out]
|
| 522 |
if self.limit_var.get() > 0:
|
| 523 |
cmd += ["--limit", str(self.limit_var.get())]
|
| 524 |
+
cmd += ["--device", self.device_var.get()]
|
| 525 |
cmd += ["--workers", str(self.workers_var.get())]
|
| 526 |
cmd += ["--cand-d", str(self.cand_d_var.get()), "--cand-m", str(self.cand_m_var.get())]
|
| 527 |
if self.optimizer_var.get() == "mse":
|
pipeline.py
CHANGED
|
@@ -139,35 +139,28 @@ def _bin_geometry():
|
|
| 139 |
|
| 140 |
|
| 141 |
def _sieve_marginals(candidate_errors, graph_blocks, stride, temperature, cfg):
|
| 142 |
-
qi_d, qi_m = _bin_geometry()
|
| 143 |
-
coarse = np.zeros((graph_blocks, 6), dtype=np.float64)
|
| 144 |
-
fine = np.zeros((graph_blocks, 6), dtype=np.float64)
|
| 145 |
n_blocks = candidate_errors.shape[0]
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
if cn > 1.0e-30:
|
| 168 |
-
coarse[gi] /= cn
|
| 169 |
-
if fn > 1.0e-30:
|
| 170 |
-
fine[gi] /= fn
|
| 171 |
|
| 172 |
amps = np.zeros((2 * graph_blocks, 6), dtype=np.float64)
|
| 173 |
amps[0::2] = coarse
|
|
@@ -889,6 +882,27 @@ class Q2KQuantizer:
|
|
| 889 |
row_width = int(x.shape[1])
|
| 890 |
x = x.ravel()
|
| 891 |
n_blocks = x.size // QK_K
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 892 |
X = x.reshape(n_blocks, QK_K)
|
| 893 |
|
| 894 |
if importance is not None:
|
|
|
|
| 139 |
|
| 140 |
|
| 141 |
def _sieve_marginals(candidate_errors, graph_blocks, stride, temperature, cfg):
|
|
|
|
|
|
|
|
|
|
| 142 |
n_blocks = candidate_errors.shape[0]
|
| 143 |
+
pad = graph_blocks * stride - n_blocks
|
| 144 |
+
ce = candidate_errors.astype(np.float64)
|
| 145 |
+
if pad > 0:
|
| 146 |
+
ce = np.concatenate([ce, np.full((pad, TOTAL_CANDIDATES), np.inf, dtype=np.float64)], axis=0)
|
| 147 |
+
grouped = ce.reshape(graph_blocks, stride, TOTAL_CANDIDATES)
|
| 148 |
+
finite = np.isfinite(grouped)
|
| 149 |
+
counts = np.sum(finite, axis=1) # (graph_blocks, 576)
|
| 150 |
+
valid = counts > 0
|
| 151 |
+
sums = np.sum(np.where(finite, grouped, 0.0), axis=1)
|
| 152 |
+
agg = np.where(valid, sums / np.maximum(counts, 1), np.inf)
|
| 153 |
+
mn = np.min(agg, axis=1, keepdims=True)
|
| 154 |
+
mn = np.where(np.isfinite(mn), mn, 0.0)
|
| 155 |
+
expo = np.where(valid, (agg - mn) / (2.0 * max(temperature, 1.0e-12)), np.inf)
|
| 156 |
+
coeff = np.exp(-expo).reshape(graph_blocks, 6, 4, 6, 4)
|
| 157 |
+
|
| 158 |
+
coarse = np.sum(coeff, axis=(2, 3, 4)) # (graph_blocks, 6)
|
| 159 |
+
fine = np.sum(coeff, axis=(1, 2, 4)) # (graph_blocks, 6)
|
| 160 |
+
cn = np.linalg.norm(coarse, axis=1, keepdims=True)
|
| 161 |
+
fn = np.linalg.norm(fine, axis=1, keepdims=True)
|
| 162 |
+
coarse = np.where(cn > 1.0e-30, coarse / np.maximum(cn, 1.0e-30), 0.0)
|
| 163 |
+
fine = np.where(fn > 1.0e-30, fine / np.maximum(fn, 1.0e-30), 0.0)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
|
| 165 |
amps = np.zeros((2 * graph_blocks, 6), dtype=np.float64)
|
| 166 |
amps[0::2] = coarse
|
|
|
|
| 882 |
row_width = int(x.shape[1])
|
| 883 |
x = x.ravel()
|
| 884 |
n_blocks = x.size // QK_K
|
| 885 |
+
|
| 886 |
+
if cfg.device != "cpu":
|
| 887 |
+
from .cuda_backend import cuda_is_available, quantize_cuda
|
| 888 |
+
|
| 889 |
+
if cfg.device.startswith("cuda") or (
|
| 890 |
+
cfg.device == "auto" and cfg.workers == 0 and n_blocks >= 32 and cuda_is_available()
|
| 891 |
+
):
|
| 892 |
+
dev_str = "cuda" if cfg.device == "auto" else cfg.device
|
| 893 |
+
res = quantize_cuda(
|
| 894 |
+
weights,
|
| 895 |
+
importance,
|
| 896 |
+
row_width,
|
| 897 |
+
cfg,
|
| 898 |
+
status_callback=status_callback,
|
| 899 |
+
stop_event=stop_event,
|
| 900 |
+
device=dev_str,
|
| 901 |
+
)
|
| 902 |
+
if cfg.verbose:
|
| 903 |
+
_report(res.stats)
|
| 904 |
+
return res
|
| 905 |
+
|
| 906 |
X = x.reshape(n_blocks, QK_K)
|
| 907 |
|
| 908 |
if importance is not None:
|
sieve.py
CHANGED
|
@@ -54,9 +54,7 @@ def sieve_measure_chain(
|
|
| 54 |
q = amps[partner] * amps[partner]
|
| 55 |
qt = float(q.sum())
|
| 56 |
q = q / qt if qt > 1.0e-30 else np.full(6, 1.0 / 6.0)
|
| 57 |
-
mix =
|
| 58 |
-
for d in range(6):
|
| 59 |
-
mix[d] = sum(q[w] * (same_penalty if w == d else 1.0) for w in range(6))
|
| 60 |
log_score += np.log(mix + 1.0e-30)
|
| 61 |
|
| 62 |
best = float(log_score.max())
|
|
|
|
| 54 |
q = amps[partner] * amps[partner]
|
| 55 |
qt = float(q.sum())
|
| 56 |
q = q / qt if qt > 1.0e-30 else np.full(6, 1.0 / 6.0)
|
| 57 |
+
mix = 1.0 - (1.0 - same_penalty) * q
|
|
|
|
|
|
|
| 58 |
log_score += np.log(mix + 1.0e-30)
|
| 59 |
|
| 60 |
best = float(log_score.max())
|
viterbi.py
CHANGED
|
@@ -5,8 +5,8 @@ This is a direct port of Phase 3 of the C engine. Each graph block owns a
|
|
| 5 |
per bin, the Sieve marginals become a log-prior bonus, and an exact DP finds
|
| 6 |
the minimum-cost state sequence with a Manhattan transition penalty.
|
| 7 |
|
| 8 |
-
|
| 9 |
-
|
| 10 |
"""
|
| 11 |
|
| 12 |
from __future__ import annotations
|
|
@@ -18,6 +18,13 @@ from .factored import CAND_TO_QUHIT, N_CAND_M, TOTAL_CANDIDATES
|
|
| 18 |
|
| 19 |
N_STATES = 36
|
| 20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
def _median_upper(values: np.ndarray) -> float:
|
| 23 |
arr = np.asarray(values)
|
|
@@ -65,45 +72,46 @@ def viterbi_select(
|
|
| 65 |
cfg: HPCConfig,
|
| 66 |
) -> tuple[np.ndarray, np.ndarray]:
|
| 67 |
n_blocks = candidate_errors.shape[0]
|
| 68 |
-
qi_d, qi_m, bins = _bin_geometry()
|
| 69 |
-
bin_err = np.full((graph_blocks, N_STATES), np.inf, dtype=np.float64)
|
| 70 |
-
bin_cand = np.full((graph_blocks, N_STATES), -1, dtype=np.int64)
|
| 71 |
-
log_prior = np.zeros((graph_blocks, N_STATES), dtype=np.float64)
|
| 72 |
temp = max(float(temperature), 1.0e-12)
|
| 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 |
finite_err = bin_err[np.isfinite(bin_err)]
|
| 109 |
scale_err = float(finite_err.mean()) if finite_err.size else 1.0e-10
|
|
@@ -117,21 +125,22 @@ def viterbi_select(
|
|
| 117 |
|
| 118 |
state_d = np.arange(N_STATES) // 6
|
| 119 |
state_m = np.arange(N_STATES) % 6
|
| 120 |
-
trans =
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
|
| 126 |
dp = np.full((graph_blocks, N_STATES), np.inf, dtype=np.float64)
|
| 127 |
back = np.zeros((graph_blocks, N_STATES), dtype=np.int64)
|
| 128 |
dp[0] = local[0]
|
|
|
|
| 129 |
for gi in range(1, graph_blocks):
|
| 130 |
totals = dp[gi - 1][:, None] + trans
|
| 131 |
-
|
| 132 |
-
best_val = totals[
|
| 133 |
dp[gi] = np.where(np.isfinite(best_val), best_val + local[gi], np.inf)
|
| 134 |
-
back[gi] =
|
| 135 |
|
| 136 |
if not np.any(np.isfinite(dp[-1])):
|
| 137 |
path = np.zeros(graph_blocks, dtype=np.int64)
|
|
@@ -141,25 +150,17 @@ def viterbi_select(
|
|
| 141 |
for gi in range(graph_blocks - 2, -1, -1):
|
| 142 |
path[gi] = back[gi + 1, path[gi + 1]]
|
| 143 |
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
if idx.size:
|
| 157 |
-
best_candidate[b] = int(idx[int(np.argmin(candidate_errors[b, idx]))])
|
| 158 |
-
|
| 159 |
-
for b in range(n_blocks):
|
| 160 |
-
cur = candidate_errors[b, best_candidate[b]]
|
| 161 |
-
g = int(np.argmin(candidate_errors[b]))
|
| 162 |
-
if candidate_errors[b, g] < cur * cfg.greedy_override_ratio:
|
| 163 |
-
best_candidate[b] = g
|
| 164 |
|
| 165 |
return best_candidate, path
|
|
|
|
| 5 |
per bin, the Sieve marginals become a log-prior bonus, and an exact DP finds
|
| 6 |
the minimum-cost state sequence with a Manhattan transition penalty.
|
| 7 |
|
| 8 |
+
Both the 576->36 bin reduction and the path->candidate mapping are fully
|
| 9 |
+
vectorised by reshaping the ``(24, 24)`` candidate grid into ``(6, 4, 6, 4)``.
|
| 10 |
"""
|
| 11 |
|
| 12 |
from __future__ import annotations
|
|
|
|
| 18 |
|
| 19 |
N_STATES = 36
|
| 20 |
|
| 21 |
+
_BIN_CAND_MAP = (
|
| 22 |
+
np.arange(TOTAL_CANDIDATES, dtype=np.int64)
|
| 23 |
+
.reshape(6, 4, 6, 4)
|
| 24 |
+
.transpose(0, 2, 1, 3)
|
| 25 |
+
.reshape(N_STATES, 16)
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
|
| 29 |
def _median_upper(values: np.ndarray) -> float:
|
| 30 |
arr = np.asarray(values)
|
|
|
|
| 72 |
cfg: HPCConfig,
|
| 73 |
) -> tuple[np.ndarray, np.ndarray]:
|
| 74 |
n_blocks = candidate_errors.shape[0]
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
temp = max(float(temperature), 1.0e-12)
|
| 76 |
|
| 77 |
+
# Reshape (n_blocks, 576) -> view (n_blocks, 6, 4, 6, 4) without copying
|
| 78 |
+
ce_f32 = np.asarray(candidate_errors, dtype=np.float32)
|
| 79 |
+
ce_view = ce_f32.reshape(n_blocks, 6, 4, 6, 4)
|
| 80 |
+
blk_bin_min = np.min(ce_view, axis=(2, 4)).reshape(n_blocks, N_STATES).astype(np.float64)
|
| 81 |
+
|
| 82 |
+
ce_bins = ce_view.transpose(0, 1, 3, 2, 4).reshape(n_blocks, N_STATES, 16)
|
| 83 |
+
blk_bin_arg = np.argmin(ce_bins, axis=2)
|
| 84 |
+
blk_bin_cand = _BIN_CAND_MAP[np.arange(N_STATES)[None, :], blk_bin_arg]
|
| 85 |
+
blk_bin_cand = np.where(np.isfinite(blk_bin_min), blk_bin_cand, -1)
|
| 86 |
+
|
| 87 |
+
pad = graph_blocks * stride - n_blocks
|
| 88 |
+
if pad > 0:
|
| 89 |
+
pad_arr = np.full((pad, 6, 4, 6, 4), np.inf, dtype=np.float32)
|
| 90 |
+
grouped = np.concatenate([ce_view, pad_arr], axis=0).reshape(graph_blocks, stride, 6, 4, 6, 4)
|
| 91 |
+
else:
|
| 92 |
+
grouped = ce_view.reshape(graph_blocks, stride, 6, 4, 6, 4)
|
| 93 |
+
|
| 94 |
+
group_min_4d = np.min(grouped, axis=(1, 3, 5)) # (graph_blocks, 6, 6)
|
| 95 |
+
group_min = group_min_4d.reshape(graph_blocks, N_STATES).astype(np.float64)
|
| 96 |
+
finite = np.isfinite(grouped)
|
| 97 |
+
counts = np.sum(finite, axis=(1, 3, 5)).reshape(graph_blocks, N_STATES)
|
| 98 |
+
valid = (counts > 0) & np.isfinite(group_min)
|
| 99 |
+
gm_b = np.where( np.isfinite(group_min_4d), group_min_4d, 0.0)[:, None, :, None, :, None]
|
| 100 |
+
shifted = np.where(finite, (grouped - gm_b) / np.float32(2.0 * temp), np.inf)
|
| 101 |
+
sums = np.sum(np.exp(-shifted), axis=(1, 3, 5), dtype=np.float64).reshape(graph_blocks, N_STATES)
|
| 102 |
+
mean_exp = np.maximum(sums / np.maximum(counts, 1), 1.0e-300)
|
| 103 |
+
bin_err = np.where(valid, group_min - 2.0 * temp * np.log(mean_exp), np.inf)
|
| 104 |
+
|
| 105 |
+
rep_idx = np.arange(graph_blocks) * stride
|
| 106 |
+
bin_cand = blk_bin_cand[rep_idx] # (graph_blocks, 36)
|
| 107 |
+
|
| 108 |
+
c_tot = np.sum(coarse_marg, axis=1, keepdims=True)
|
| 109 |
+
f_tot = np.sum(fine_marg, axis=1, keepdims=True)
|
| 110 |
+
pc = np.where(c_tot > 1.0e-30, coarse_marg / np.maximum(c_tot, 1.0e-30), 1.0 / 6.0)
|
| 111 |
+
pf = np.where(f_tot > 1.0e-30, fine_marg / np.maximum(f_tot, 1.0e-30), 1.0 / 6.0)
|
| 112 |
+
log_prior = (
|
| 113 |
+
np.log(pc[:, :, None] + 1.0e-30) + np.log(pf[:, None, :] + 1.0e-30)
|
| 114 |
+
).reshape(graph_blocks, N_STATES)
|
| 115 |
|
| 116 |
finite_err = bin_err[np.isfinite(bin_err)]
|
| 117 |
scale_err = float(finite_err.mean()) if finite_err.size else 1.0e-10
|
|
|
|
| 125 |
|
| 126 |
state_d = np.arange(N_STATES) // 6
|
| 127 |
state_m = np.arange(N_STATES) % 6
|
| 128 |
+
trans = (
|
| 129 |
+
cfg.viterbi_alpha
|
| 130 |
+
* scale_err
|
| 131 |
+
* (np.abs(state_d[None, :] - state_d[:, None]) + np.abs(state_m[None, :] - state_m[:, None]))
|
| 132 |
+
)
|
| 133 |
|
| 134 |
dp = np.full((graph_blocks, N_STATES), np.inf, dtype=np.float64)
|
| 135 |
back = np.zeros((graph_blocks, N_STATES), dtype=np.int64)
|
| 136 |
dp[0] = local[0]
|
| 137 |
+
s_range = np.arange(N_STATES)
|
| 138 |
for gi in range(1, graph_blocks):
|
| 139 |
totals = dp[gi - 1][:, None] + trans
|
| 140 |
+
best_sp = np.argmin(totals, axis=0)
|
| 141 |
+
best_val = totals[best_sp, s_range]
|
| 142 |
dp[gi] = np.where(np.isfinite(best_val), best_val + local[gi], np.inf)
|
| 143 |
+
back[gi] = best_sp
|
| 144 |
|
| 145 |
if not np.any(np.isfinite(dp[-1])):
|
| 146 |
path = np.zeros(graph_blocks, dtype=np.int64)
|
|
|
|
| 150 |
for gi in range(graph_blocks - 2, -1, -1):
|
| 151 |
path[gi] = back[gi + 1, path[gi + 1]]
|
| 152 |
|
| 153 |
+
# Vectorized path -> block candidate mapping:
|
| 154 |
+
blk_gi = np.arange(n_blocks) // stride
|
| 155 |
+
chosen_state = path[blk_gi]
|
| 156 |
+
chosen_cand = blk_bin_cand[np.arange(n_blocks), chosen_state]
|
| 157 |
+
global_best_cand = np.argmin(candidate_errors, axis=1).astype(np.int64)
|
| 158 |
+
best_candidate = np.where(chosen_cand >= 0, chosen_cand, global_best_cand)
|
| 159 |
+
|
| 160 |
+
cur_err = candidate_errors[np.arange(n_blocks), best_candidate]
|
| 161 |
+
g_err = candidate_errors[np.arange(n_blocks), global_best_cand]
|
| 162 |
+
best_candidate = np.where(
|
| 163 |
+
g_err < cur_err * cfg.greedy_override_ratio, global_best_cand, best_candidate
|
| 164 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 165 |
|
| 166 |
return best_candidate, path
|