File size: 3,124 Bytes
c881b77 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | """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()
|