"""Claim 5 (faithful, scaled): Raisa et al. synthetic benchmark mechanism. Uses the OFFICIAL GICDM repo (github.com/nicolassalvy/GICDM) Clipped Density / Clipped Coverage with the standard vs GICDM DataProcessor. We run representative Raisa scenarios on modest synthetic data (scaled down N,d) and tally whether each metric behaves correctly with/without GICDM. We report the *direction* of change and note the paper's full pass counts (Clipped Density Purpose 8/14 -> 10/14, Bounds 8/13 -> 11/13; Clipped Coverage 8/14->10/14, 9/13->11/13). """ import numpy as np import sys, os sys.path.insert(0, "/Users/equan_p/Developer/playground/ICML-2/official/GICDM") from metrics.hubness_processor.standard import DataProcessorStandard from metrics.hubness_processor.gicdm import GICDM from metrics.metrics.clipped_density_coverage import ClippedDensityCoverage def make_processor(Xr, use_gicdm, K=5): if use_gicdm: return GICDM(Xr, K=K, n_jobs=4, scale_factor=10) return DataProcessorStandard(Xr, K=K, n_jobs=4) def cd_cc(Xr, Xg, use_gicdm, K=5): dp = make_processor(Xr, use_gicdm, K) cdc = ClippedDensityCoverage(dp) cd = cdc.clipped_density(Xg) cc = cdc.clipped_coverage(Xg) return float(cd), float(cc) def sphere(d, n, r, rng): v = rng.normal(size=(n, d)); v /= np.linalg.norm(v, axis=1, keepdims=True) return v * r def main(): rng = np.random.default_rng(0) d, n = 100, 1500 rows = [] scenarios = {} # 1. GAUSSIAN MEAN DIFFERENCE: shift=0 -> identical -> CD,CC should be ~1 Xr = rng.normal(0, 1, size=(n, d)); Xg0 = rng.normal(0, 1, size=(n, d)) scenarios['gauss_mean_equal'] = (Xr.copy(), Xg0.copy()) # 2. GAUSSIAN STD DEVIATION DIFFERENCE: scale mismatch Xg_std = rng.normal(0, 1.5, size=(n, d)) scenarios['gauss_std_1p5'] = (Xr.copy(), Xg_std.copy()) # 3. HYPERSPHERE SURFACE equal radius (should be ~1) sr = sphere(d, n, 1.0, rng); sg = sphere(d, n, 1.0, rng) scenarios['hypersphere_equal'] = (sr, sg) # 4. MODE COLLAPSE: gen collapses to one of 5 modes centers = rng.normal(0, 5, size=(5, d)) Xr_m = centers[rng.integers(0, 5, n)] + rng.normal(0, 0.3, (n, d)) Xg_mc = centers[0] + rng.normal(0, 0.3, (n, d)) scenarios['mode_collapse'] = (Xr_m.copy(), Xg_mc.copy()) Xg_mf = centers[rng.integers(0, 5, n)] + rng.normal(0, 0.3, (n, d)) scenarios['mode_full'] = (Xr_m.copy(), Xg_mf.copy()) # 5. SPHERE vs TORUS-like (sphere vs scaled sphere offset) st = sphere(d, n, 1.0, rng) + 3.0 # offset sphere -> out of manifold scenarios['sphere_offset'] = (sr.copy(), st.copy()) results = {} for name, (Xr_, Xg_) in scenarios.items(): cd0, cc0 = cd_cc(Xr_, Xg_, False) cdg, ccg = cd_cc(Xr_, Xg_, True) results[name] = dict(cd_raw=cd0, cc_raw=cc0, cd_gicdm=cdg, cc_gicdm=ccg) print(f"{name:18s} CD raw={cd0:.3f} GICDM={cdg:.3f} | CC raw={cc0:.3f} GICDM={ccg:.3f}") import json json.dump(results, open("/Users/equan_p/Developer/playground/ICML-2/repro_gicdm/results/claim5_official.json", "w")) if __name__ == "__main__": main()