"""Pair saved samples by forcing phase, never by the size of output errors. All curves share ONE pair of row indices. No interpolation across jumps, no time shifting, and no use of force/pressure agreement to select a sample. The reference's nearest saved grid row is authoritative; a different native row is allowed only in the tiny output-timestamp roundoff window. """ from __future__ import annotations import numpy as np def pair_saved_phases(native_times, reference_times, native_signals, reference_signals, grid, step): nt, rt, grid = map(np.asarray, (native_times, reference_times, grid)) ns, rs = map(np.asarray, (native_signals, reference_signals)) if ns.shape != (len(nt), rs.shape[1]) or len(rs) != len(rt): raise ValueError('Phase signatures must be rows by the same signal columns.') tolerance = step * 1e-7 ni = np.full(len(grid), -1, dtype=int) ri = np.full(len(grid), -1, dtype=int) records = [] for j, t in enumerate(grid): candidates = np.arange(np.searchsorted(nt, t-tolerance, side='left'), np.searchsorted(nt, t+tolerance, side='right')) references = np.arange(np.searchsorted(rt, t-tolerance, side='left'), np.searchsorted(rt, t+tolerance, side='right')) if not len(candidates) or not len(references): records.append(dict(gridIndex=j, time=float(t), status='missing-saved-sample')) continue # Prefer the later row on an exact tie / duplicate timestamp, matching # the comparison's existing right-side duplicate policy. ref = min(references, key=lambda i: (abs(rt[i]-t), -int(i))) closest = min(candidates, key=lambda i: (abs(nt[i]-t), -int(i))) matches = candidates[np.all(np.isclose(ns[candidates], rs[ref], rtol=1e-12, atol=1e-12), axis=1)] ri[j] = ref if not len(matches): records.append(dict(gridIndex=j, time=float(t), status='unmatched-forcing-phase', referenceTime=float(rt[ref]), referenceSignals=rs[ref].tolist(), nativeCandidateTimes=nt[candidates].tolist(), nativeCandidateSignals=ns[candidates].tolist())) continue chosen = min(matches, key=lambda i: (abs(nt[i]-t), -int(i))) ni[j] = chosen if chosen != closest: records.append(dict(gridIndex=j, time=float(t), status='matched-other-event-side', platformTime=float(nt[chosen]), referenceTime=float(rt[ref]), originalPlatformTime=float(nt[closest]), signals=ns[chosen].tolist(), referenceSignals=rs[ref].tolist())) return ni, ri, records