Files
wehub-resource-sync 426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

219 lines
7.0 KiB
Python

"""PolarQuant: optimal Gaussian weight quantization via Hadamard rotation.
Pipeline:
1. Per-block L2 normalize (block_size must be a power of 2).
2. Walsh-Hadamard rotation -> coordinates ~ N(0, 1/d).
3. Lloyd-Max optimal scalar quantization for N(0, 1).
4. Optional 1-bit QJL residual sign correction.
Storage layout: int8 codes + fp16 per-block norms (+ 1-bit QJL signs).
"""
from __future__ import annotations
import math
import sys
from dataclasses import dataclass
from pathlib import Path
import numpy as np
import torch
import torch.nn.functional as F
from scipy.stats import norm
# Import the canonical xorshift32 helper (sibling of the polarquant/ package).
_HERE = Path(__file__).resolve().parent
_QUANT_DIR = _HERE.parent
if str(_QUANT_DIR) not in sys.path:
sys.path.insert(0, str(_QUANT_DIR))
from polar_xorshift32 import polar_xorshift32_signs # noqa: E402
# Pre-computed Lloyd-Max centroids for N(0,1). Keyed by bits; entries stay
# None until first use (computed on demand by ``_ensure_centroids``).
_GAUSSIAN_CENTROIDS: dict[int, torch.Tensor | None] = {
2: torch.tensor([-1.5104, -0.4528, 0.4528, 1.5104]),
3: torch.tensor(
[-2.1520, -1.3440, -0.7560, -0.2451, 0.2451, 0.7560, 1.3440, 2.1520]
),
4: None,
5: None,
6: None,
}
_HADAMARD_CACHE: dict[int, torch.Tensor] = {}
# Magnitude of the QJL residual correction along the random projection
# direction. Small enough to never overshoot the Lloyd-Max cell.
_QJL_CORRECTION_MAGNITUDE = 0.5
# Deterministic seed for the per-block random sign vector used by the QJL
# correction. The encoder and decoder must agree on it.
_QJL_SEED = 42
def _compute_lloyd_max_centroids(n_levels: int, n_iter: int = 100) -> torch.Tensor:
"""Iteratively solve for MSE-optimal scalar quantizer levels for N(0,1).
Conditional expectation in [a, b] for X ~ N(0,1):
E[X | a < X < b] = (phi(a) - phi(b)) / (Phi(b) - Phi(a))
"""
boundaries = np.linspace(-4, 4, n_levels + 1)
boundaries[0] = -np.inf
boundaries[-1] = np.inf
centroids = np.zeros(n_levels)
for _ in range(n_iter):
for i in range(n_levels):
a, b = boundaries[i], boundaries[i + 1]
prob = norm.cdf(b) - norm.cdf(a)
if prob > 1e-10:
centroids[i] = (norm.pdf(a) - norm.pdf(b)) / prob
else:
centroids[i] = (a + b) / 2
new_boundaries = np.zeros(n_levels + 1)
new_boundaries[0] = -np.inf
new_boundaries[-1] = np.inf
for i in range(1, n_levels):
new_boundaries[i] = (centroids[i - 1] + centroids[i]) / 2
boundaries = new_boundaries
return torch.tensor(centroids, dtype=torch.float32)
def _ensure_centroids(bits: int) -> torch.Tensor:
if bits not in _GAUSSIAN_CENTROIDS:
raise ValueError(
f"PolarQuant supports bits in {sorted(_GAUSSIAN_CENTROIDS)}; got {bits}"
)
cached = _GAUSSIAN_CENTROIDS[bits]
if cached is None:
cached = _compute_lloyd_max_centroids(1 << bits)
_GAUSSIAN_CENTROIDS[bits] = cached
return cached
def _hadamard_matrix(n: int, device: torch.device | None = None) -> torch.Tensor:
"""Walsh-Hadamard matrix of size n (power of 2), normalized so H @ H.T == I."""
if n & (n - 1) != 0:
raise ValueError(f"Hadamard size must be a power of 2; got {n}")
cached = _HADAMARD_CACHE.get(n)
if cached is None:
if n == 1:
cached = torch.tensor([[1.0]])
else:
h = _hadamard_matrix(n // 2)
cached = torch.cat(
[torch.cat([h, h], 1), torch.cat([h, -h], 1)], 0
) / math.sqrt(2)
_HADAMARD_CACHE[n] = cached
if device is not None:
return cached.to(device)
return cached
@dataclass
class PolarQuantResult:
"""Result of PolarQuant compression for one tensor."""
codes: torch.Tensor # int8, length n_elements
norms: torch.Tensor # fp16, length n_blocks
bits: int
block_size: int
shape: torch.Size
n_elements: int
use_qjl: bool
qjl_signs: torch.Tensor | None = None # uint8, length n_blocks
def polar_quantize(
weight: torch.Tensor,
bits: int = 4,
block_size: int = 128,
use_qjl: bool = True,
) -> PolarQuantResult:
"""Encode ``weight`` into PolarQuant codes."""
centroids = _ensure_centroids(bits).to(weight.device)
H = _hadamard_matrix(block_size, weight.device)
flat = weight.detach().float().flatten()
n = flat.numel()
pad = (block_size - n % block_size) % block_size
if pad > 0:
flat = F.pad(flat, (0, pad))
blocks = flat.view(-1, block_size)
norms = blocks.norm(dim=1, keepdim=True).clamp(min=1e-10)
blocks_norm = blocks / norms
blocks_rot = blocks_norm @ H
# After rotation each coord is ~ N(0, 1/sqrt(d)); scale to N(0, 1)
# so the Lloyd-Max centroids apply directly.
scale = math.sqrt(block_size)
blocks_scaled = blocks_rot * scale
diffs = blocks_scaled.unsqueeze(-1) - centroids.unsqueeze(0).unsqueeze(0)
codes = diffs.abs().argmin(dim=-1).to(torch.int8)
qjl_signs: torch.Tensor | None = None
if use_qjl:
recon_scaled = centroids[codes.long()]
residual = blocks_scaled - recon_scaled
random_signs = torch.from_numpy(
polar_xorshift32_signs(block_size, _QJL_SEED)
).to(dtype=torch.float32, device=weight.device)
projections = (residual * random_signs.unsqueeze(0)).sum(dim=1)
qjl_signs = (projections >= 0).to(torch.uint8)
return PolarQuantResult(
codes=codes.flatten()[:n],
norms=norms.squeeze(1).to(torch.float16),
bits=bits,
block_size=block_size,
shape=weight.shape,
n_elements=n,
use_qjl=use_qjl,
qjl_signs=qjl_signs,
)
def polar_dequantize(
result: PolarQuantResult, device: torch.device | None = None
) -> torch.Tensor:
"""Decode a PolarQuant payload back to fp16."""
if device is None:
device = result.codes.device
centroids = _ensure_centroids(result.bits).to(device)
H = _hadamard_matrix(result.block_size, device)
bs = result.block_size
n = result.n_elements
pad = (bs - n % bs) % bs
codes = result.codes.to(device).long()
if pad > 0:
codes = F.pad(codes, (0, pad))
blocks_codes = codes.view(-1, bs)
recon_scaled = centroids[blocks_codes]
if result.use_qjl and result.qjl_signs is not None:
random_signs = torch.from_numpy(
polar_xorshift32_signs(bs, _QJL_SEED)
).to(dtype=torch.float32, device=device)
correction_dir = random_signs.unsqueeze(0) / math.sqrt(bs)
correction_sign = result.qjl_signs.float().to(device) * 2 - 1
recon_scaled = recon_scaled + (
_QJL_CORRECTION_MAGNITUDE
* correction_sign.unsqueeze(1)
* correction_dir
)
scale = math.sqrt(bs)
recon_rot = recon_scaled / scale
recon_norm = recon_rot @ H
norms = result.norms.float().to(device)
recon = recon_norm * norms.unsqueeze(1)
return recon.flatten()[:n].view(result.shape).half()