#!/usr/bin/env python3
# Differential check of stage D: our csetmarg output vs Linas's state-D, over every value on the pseudo-cset
# marginal atoms (ListLinks holding AnyNode "cset-word" / "cset-disjunct") under the ported keys.
# Values to a relative 1e-9. diffd.py <linas-state-D.tsv> <ours.tsv>
import math, sys
KEYS = {'*-Norm Key-*', '*-MM^T Product Key-*', '*-Left Total Key-*', '*-Right Total Key-*', '*-Supp Dimension Key-*'}
def load(path):
d = {}
for line in open(path):
parts = line.rstrip('\n').split('\t')
if len(parts) != 3: continue
atom, key, vals = parts
if not atom.startswith('(ListLink ') or ('"cset-word"' not in atom and '"cset-disjunct"' not in atom): continue
k = key[len('(PredicateNode "'):-2]
if k not in KEYS: continue
d[(atom, k)] = [float(x) for x in vals.split()]
return d
def same(a, b):
return len(a) == len(b) and all(abs(x - y) <= 1e-9 * max(abs(x), abs(y)) or abs(x - y) <= 1e-12 for x, y in zip(a, b))
L, O = load(sys.argv[1]), load(sys.argv[2])
only_l, only_o = sorted(set(L) - set(O)), sorted(set(O) - set(L))
bad = [k for k in sorted(set(L) & set(O)) if not same(L[k], O[k])]
by = {}
for k in set(L) & set(O): by[k[1]] = by.get(k[1], 0) + 1
print(f'linas {len(L)} ours {len(O)} both {len(set(L) & set(O))} only-linas {len(only_l)} only-ours {len(only_o)} value-mismatch {len(bad)}')
for key, n in sorted(by.items()): print(f' {key}: {n}')
for k in only_l[:4]: print('ONLY-LINAS', k)
for k in only_o[:4]: print('ONLY-OURS', k)
for k in bad[:6]: print('MISMATCH', k, L[k], O[k])
sys.exit(0 if not (only_l or only_o or bad) else 1)