Download data/ot_coupling.py from MissawB/creature-flow-point-dit: direct link, hf CLI and curl.
- Browser
- Download file 1.65 kB
-
https://huggingface.co/MissawB/creature-flow-point-dit/resolve/main/data/ot_coupling.py
- Command line
-
hf download hf://MissawB/creature-flow-point-dit/data/ot_coupling.py
-
curl -L -o ot_coupling.py https://huggingface.co/MissawB/creature-flow-point-dit/resolve/main/data/ot_coupling.py
1.65 kB
| """ | |
| Per-shape optimal transport coupling between Gaussian noise and surface points. | |
| Pairing each noise point with a surface point (instead of index i with index i) removes the | |
| permutation ambiguity of independent coupling and yields straighter flow trajectories. | |
| """ | |
| import numpy as np | |
| from concurrent.futures import ProcessPoolExecutor | |
| from typing import List, Optional | |
| from scipy.optimize import linear_sum_assignment | |
| from scipy.spatial.distance import cdist | |
| def ot_match_noise(points: np.ndarray, rng: np.random.Generator) -> np.ndarray: | |
| """Draws N(0, I) noise and reorders it so noise[i] is the exact OT partner of points[i].""" | |
| noise = rng.standard_normal(points.shape).astype(np.float32) | |
| noise_idx, point_idx = linear_sum_assignment(cdist(noise, points, "sqeuclidean")) | |
| matched = np.empty_like(noise) | |
| matched[point_idx] = noise[noise_idx] | |
| return matched | |
| def _match_many(args) -> np.ndarray: | |
| points, num_noise, seed = args | |
| rng = np.random.default_rng(seed) | |
| return np.stack([ot_match_noise(points, rng) for _ in range(num_noise)]) | |
| def precompute_ot_noise( | |
| all_points: List[np.ndarray], | |
| num_noise: int, | |
| seed: int = 0, | |
| num_workers: Optional[int] = None | |
| ) -> List[np.ndarray]: | |
| """Returns, for each shape, a (num_noise, N, 3) stack of OT-matched noise. Exact matching costs ~2 s per 2048-point shape.""" | |
| jobs = [(points, num_noise, seed + i) for i, points in enumerate(all_points)] | |
| if num_workers == 1 or len(jobs) <= 1: | |
| return [_match_many(job) for job in jobs] | |
| with ProcessPoolExecutor(max_workers=num_workers) as pool: | |
| return list(pool.map(_match_many, jobs)) | |