Instructions to use ApacheOne/Wan2.2-Animate-2-14B-OrbitQuant-W4A4 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use ApacheOne/Wan2.2-Animate-2-14B-OrbitQuant-W4A4 with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline from diffusers.utils import load_image, export_to_video # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("ApacheOne/Wan2.2-Animate-2-14B-OrbitQuant-W4A4", dtype=torch.bfloat16, device_map="cuda") pipe.to("cuda") prompt = "A man with short gray hair plays a red electric guitar." image = load_image( "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/guitar-man.png" ) output = pipe(image=image, prompt=prompt).frames[0] export_to_video(output, "output.mp4") - Notebooks
- Google Colab
- Kaggle
| from __future__ import annotations | |
| import math | |
| from typing import Iterable, Tuple | |
| import numpy as np | |
| import torch | |
| from scipy.special import betainc, betaincinv, gammaln | |
| EPS = 1e-10 | |
| def largest_power_of_two_divisor(d: int) -> int: | |
| if d <= 0: | |
| raise ValueError(f"d must be positive, got {d}") | |
| return d & -d | |
| def coordinate_cdf(t: np.ndarray | float, d: int) -> np.ndarray: | |
| x = np.asarray(t, dtype=np.float64) | |
| x = np.clip(x, -1.0, 1.0) | |
| a = (d - 1.0) / 2.0 | |
| z = betainc(0.5, a, x * x) | |
| return np.where(x >= 0.0, 0.5 + 0.5 * z, 0.5 - 0.5 * z) | |
| def coordinate_ppf(p: np.ndarray | float, d: int) -> np.ndarray: | |
| q = np.asarray(p, dtype=np.float64) | |
| q = np.clip(q, np.finfo(np.float64).eps, 1.0 - np.finfo(np.float64).eps) | |
| a = (d - 1.0) / 2.0 | |
| upper = q >= 0.5 | |
| z = np.empty_like(q) | |
| z[upper] = betaincinv(0.5, a, 2.0 * q[upper] - 1.0) | |
| z[~upper] = betaincinv(0.5, a, 1.0 - 2.0 * q[~upper]) | |
| ans = np.sqrt(np.clip(z, 0.0, 1.0)) | |
| ans[~upper] *= -1.0 | |
| return ans | |
| def interval_probability(lo: float, hi: float, d: int) -> float: | |
| return float(coordinate_cdf(hi, d) - coordinate_cdf(lo, d)) | |
| def interval_first_moment(lo: float, hi: float, d: int) -> float: | |
| a = (d - 1.0) / 2.0 | |
| log_c = gammaln(d / 2.0) - 0.5 * math.log(math.pi) - gammaln((d - 1.0) / 2.0) | |
| c = math.exp(log_c) | |
| lterm = max(0.0, 1.0 - lo * lo) ** a | |
| hterm = max(0.0, 1.0 - hi * hi) ** a | |
| return c / (d - 1.0) * (lterm - hterm) | |
| def lloyd_max_codebook(d: int, bits: int = 4, tol: float = 1e-13, max_iter: int = 500) -> np.ndarray: | |
| """Deterministic Lloyd-Max solver for OrbitQuant's exact coordinate density f_d.""" | |
| if d < 2: | |
| raise ValueError("d must be >= 2") | |
| if bits < 1 or bits > 8: | |
| raise ValueError("bits must be in [1,8]") | |
| levels = 1 << bits | |
| probs = (np.arange(levels, dtype=np.float64) + 0.5) / levels | |
| centroids = coordinate_ppf(probs, d) | |
| centroids = 0.5 * (centroids - centroids[::-1]) | |
| for _ in range(max_iter): | |
| edges = np.empty(levels + 1, dtype=np.float64) | |
| edges[0], edges[-1] = -1.0, 1.0 | |
| edges[1:-1] = 0.5 * (centroids[:-1] + centroids[1:]) | |
| updated = np.empty_like(centroids) | |
| for i in range(levels): | |
| lo, hi = float(edges[i]), float(edges[i + 1]) | |
| mass = interval_probability(lo, hi, d) | |
| updated[i] = centroids[i] if mass <= 1e-300 else interval_first_moment(lo, hi, d) / mass | |
| updated = 0.5 * (updated - updated[::-1]) | |
| if np.max(np.abs(updated - centroids)) < tol: | |
| centroids = updated | |
| break | |
| centroids = updated | |
| if not np.all(np.diff(centroids) > 0): | |
| raise RuntimeError(f"non-monotonic Lloyd-Max codebook for d={d}") | |
| return centroids.astype(np.float64) | |
| def make_rotation(d: int, seed: int) -> Tuple[np.ndarray, np.ndarray, int]: | |
| # Exact deterministic construction used by this Project-A runtime. | |
| # The OrbitQuant paper does not publish the authors' random seed. | |
| ss = np.random.SeedSequence([int(seed), int(d), 0x4F524249]) | |
| rng = np.random.default_rng(ss) | |
| perm = rng.permutation(d).astype(np.int64) | |
| signs = rng.choice(np.array([-1, 1], dtype=np.int8), size=d, replace=True) | |
| return perm, signs, largest_power_of_two_divisor(d) | |
| def fwht_last_dim(x: torch.Tensor, block_size: int) -> torch.Tensor: | |
| if block_size == 1: | |
| return x | |
| if block_size <= 0 or block_size & (block_size - 1): | |
| raise ValueError(f"block_size must be power-of-two, got {block_size}") | |
| d = int(x.shape[-1]) | |
| if d % block_size: | |
| raise ValueError(f"last dimension {d} not divisible by {block_size}") | |
| lead = x.shape[:-1] | |
| y = x.reshape(*lead, d // block_size, block_size) | |
| step = 1 | |
| while step < block_size: | |
| shape = y.shape | |
| z = y.reshape(*shape[:-1], -1, 2, step) | |
| a, b = z[..., 0, :], z[..., 1, :] | |
| y = torch.stack((a + b, a - b), dim=-2).reshape(*shape) | |
| step *= 2 | |
| return (y / math.sqrt(block_size)).reshape(*lead, d) | |
| def nearest_codes(x: torch.Tensor, codebook: torch.Tensor) -> torch.Tensor: | |
| cb = codebook.to(device=x.device, dtype=x.dtype) | |
| thresholds = 0.5 * (cb[:-1] + cb[1:]) | |
| return torch.bucketize(x.contiguous(), thresholds).to(torch.uint8) | |