#!/usr/bin/env python3 """Attempt 018: frozen held-out recording validation and matched-component mixture intervention.""" import hashlib,itertools,json,math,os,time import os from pathlib import Path os.environ.setdefault('OPENBLAS_NUM_THREADS','1');os.environ.setdefault('OMP_NUM_THREADS','1') import numpy as np import pandas as pd import soundfile as sf from scipy.signal import resample_poly from ai_edge_litert.interpreter import Interpreter ROUND=Path(__file__).resolve().parents[1];PROJECT=ROUND.parent CACHE=Path(os.environ.get('BIO_DISCOVERY_CACHE','/mnt/data/research/bioinformatics-discovery/cache'));OUTPUT=CACHE/'round-002';OUTPUT.mkdir(exist_ok=True) def rank(value):return hashlib.sha256(f'015:20260919:{value}'.encode()).hexdigest() def main(): started=time.monotonic();data=pd.read_csv(ROUND/'evidence/ceb_v1_1_test_soundscape.csv');data['event_id']=np.arange(len(data)) data=data[(data.label_status=='valid')&(data.end_time>data.start_time)].copy() labels=(CACHE/'v1_5_1_BirdNET_GLOBAL_6K_V2.4_Labels_en_uk.txt').read_text().splitlines();mapping={x.split('_')[0]:i for i,x in enumerate(labels)} options=[] for file,group in data.groupby('filepath'): if int(rank(file)[:8],16)%3==0:continue for e in group.itertuples(): duration=e.end_time-e.start_time if e.scientific_name not in mapping:continue start=(e.start_time+e.end_time)/2-.6;end=start+1.2 if start<0 or end>180:continue overlap=group[(group.start_timestart)] if set(overlap.scientific_name)!={e.scientific_name}:continue options.append(dict(event_id=int(e.event_id),filepath=file,species=e.scientific_name,start=start,end=end,duration=duration,selection_hash=rank(e.event_id))) candidates=pd.DataFrame(options) if candidates.empty:raise ValueError('No eligible isolated events') # At most twelve distinct-file candidates/species, before inspecting scores. candidates=candidates.sort_values('selection_hash').drop_duplicates(['species','filepath']).groupby('species',group_keys=False).head(12).sort_values('selection_hash').head(360) candidates.to_csv(ROUND/'results/018_validation_candidate_sample.csv',index=False) clips={} for file,group in candidates.groupby('filepath'): audio,rate=sf.read(CACHE/'ceb_test_audio'/file,dtype='float32',always_2d=True);audio=audio.mean(axis=1) gcd=math.gcd(rate,48000);audio=resample_poly(audio,48000//gcd,rate//gcd).astype('float32') for e in group.itertuples(): clip=audio[round(e.start*48000):round(e.start*48000)+57600].copy();assert len(clip)==57600 clip-=clip.mean();fade=np.linspace(0,1,240,dtype='float32');clip[:240]*=fade;clip[-240:]*=fade[::-1] rms=float(np.sqrt(np.mean(clip**2)));assert rms>0;clip*=.03/rms;clips[e.event_id]=clip # A single scale across all clips preserves matched RMS and prevents clipping. scale=min(1.,.6/max(float(np.abs(c).max()) for c in clips.values())) clips={k:(v*scale*.5).astype('float32') for k,v in clips.items()} model_path=CACHE/'v1_5_1_BirdNET_GLOBAL_6K_V2.4_Model_FP32.tflite' model=Interpreter(model_path=str(model_path),num_threads=1);model.allocate_tensors();inp=model.get_input_details()[0];out=model.get_output_details()[0] def predict(audio): assert len(audio)==144000 and np.abs(audio).max()<1 model.set_tensor(inp['index'],audio[None,:]);model.invoke();logits=model.get_tensor(out['index'])[0] return 1/(1+np.exp(-np.clip(logits,-15,15))) def window(event,position): wave=np.zeros(144000,dtype='float32');start={0:7200,1:79200}[position];wave[start:start+57600]=clips[event];return wave baseline=[];base_scores={} for e in candidates.itertuples(): row=e._asdict();row.pop('Index',None) for position in [0,1]: scores=predict(window(e.event_id,position));base_scores[e.event_id,position]=scores row[f'score_{position}']=float(scores[mapping[e.species]]) row['peak']=float(np.abs(clips[e.event_id]).max());row['rms']=float(np.sqrt(np.mean(clips[e.event_id]**2)));baseline.append(row) baseline=pd.DataFrame(baseline);baseline.to_csv(ROUND/'results/018_validation_all_baselines.csv',index=False) selected=baseline[(baseline.score_0>=.5)&(baseline.score_1>=.5)].sort_values('selection_hash').groupby('species',group_keys=False).head(4) counts=selected.groupby('species').size();selected=selected[selected.species.isin(counts.index[counts>=3])] # Deterministic bounded species sample if eligibility is unexpectedly large. species=sorted(selected.species.unique(),key=rank)[:16];selected=selected[selected.species.isin(species)] selected.to_csv(ROUND/'results/018_validation_selected_events.csv',index=False) np.savez_compressed(OUTPUT/'018_validation_clips.npz',**{str(k):v for k,v in clips.items() if k in set(selected.event_id)}) np.savez_compressed(OUTPUT/'018_validation_baseline_scores.npz',**{f'{k[0]}_{k[1]}':v for k,v in base_scores.items() if k[0] in set(selected.event_id)}) silent=predict(np.zeros(144000,dtype='float32'));np.save(OUTPUT/'018_validation_silence.npy',silent) print(f'Candidates {len(candidates)}, selected {len(selected)} events / {len(species)} species',flush=True) rows=[] for a,b in itertools.combinations(selected.to_dict('records'),2): if a['filepath']==b['filepath']:continue for condition,pa,pb in [('nonoverlap_AB',0,1),('nonoverlap_BA',1,0),('simultaneous',0,0)]: wa=window(a['event_id'],pa);wb=window(b['event_id'],pb);scores=predict(wa+wb) for target,other,pos,otherpos in [(a,b,pa,pb),(b,a,pb,pa)]: ix=mapping[target['species']] rows.append(dict(event_id=target['event_id'],species=target['species'],file=target['filepath'],other_event=other['event_id'],other_species=other['species'],other_file=other['filepath'],same_species=target['species']==other['species'],condition=condition,baseline_score=float(base_scores[target['event_id'],pos][ix]),other_alone_score=float(base_scores[other['event_id'],otherpos][ix]),mixed_score=float(scores[ix]),position=pos)) result=pd.DataFrame(rows);result.to_csv(ROUND/'results/018_validation_mixture_results.csv',index=False) summaries=[] if len(result): for condition,group in result[~result.same_species].groupby('condition'): lost=group[group.mixed_score<.5] affected=lost.groupby('species').apply(lambda x:x[['file','other_file']].drop_duplicates().shape[0]) if len(lost) else pd.Series(dtype=int) summaries.append(dict(condition=condition,target_appearances=len(group),losses=int(len(lost)),loss_fraction=float(len(lost)/len(group)),median_score_change=float((group.mixed_score-group.baseline_score).median()),affected_species=int(len(affected)),species_with_two_recording_pairs=int((affected>=2).sum()))) non=result[result.condition.str.startswith('nonoverlap')&~result.same_species] if len(result) else result affected=non[non.mixed_score<.5].groupby('species').apply(lambda x:x[['file','other_file']].drop_duplicates().shape[0]) if len(non) and (non.mixed_score<.5).any() else pd.Series(dtype=int) summary=dict(candidate_events=len(candidates),selected_events=len(selected),selected_species=len(species),global_scale=scale,conditions=summaries, advance=bool(len(species)>=8 and len(selected)>=24 and len(non) and (non.mixed_score<.5).mean()>=.2 and (affected>=2).sum()>=4), runtime_seconds=time.monotonic()-started,model_sha256=hashlib.sha256(model_path.read_bytes()).hexdigest(), interpretation='Counterfactual classifier detectability pilot, not evidence of ecological competition. Baselines use exactly the same component gain and position; nonoverlap clips occupy disjoint 1.2-second intervals separated by 0.3 seconds.') (ROUND/'results/018_validation_summary.json').write_text(json.dumps(summary,indent=2)+'\n');print(json.dumps(summary,indent=2),flush=True) if __name__=='__main__':main()