import itertools,unittest,math import numpy as np from irstd.core import * class CoreTests(unittest.TestCase): def test_matching_not_greedy(self): self.assertEqual(len(matching([[1,1],[1,0]])),2) def test_empty_matching(self): self.assertEqual(matching(np.zeros((0,2),bool)),[]) self.assertEqual(matching(np.zeros((2,0),bool)),[]) def test_adjacent_targets_conflict(self): _,selected=exact_mwis([.9,.8],[(0,1)]) self.assertEqual(len(selected),1) self.assertEqual(len(exact_mwis([.9,.8],[])[1]),2) def test_invalid_recall_even_with_feasible_target(self): self.assertEqual(exact_mwis([1.,2.],[(0,1)])[1],[1]) def test_pixel_threshold_splits_false_object(self): p=np.array([[.9,.6,.9]]) self.assertEqual(len(components(p>=.5)),1) self.assertEqual(len(components(p>=.8)),2) def test_matching_deficiency_exhaustive(self): # Exhaust all 3x2 eligibility graphs and all subsets. for flat in itertools.product([0,1],repeat=6): e=np.array(flat).reshape(3,2) for bits in itertools.product([0,1],repeat=3): s=np.flatnonzero(bits);fp=len(s)-len(matching(e[s])) for j in set(range(3))-set(s): t=np.append(s,j);self.assertLessEqual(fp,len(t)-len(matching(e[t]))) def test_canonical_bound_exhaustive(self): for flat in itertools.product([0,1],repeat=6): e=np.array(flat).reshape(3,2);pos={i for i,_ in matching(e)} for bits in itertools.product([0,1],repeat=3): s=np.flatnonzero(bits) self.assertLessEqual(len(s)-len(matching(e[s])),len(set(s)-pos)) def test_surrogate_upper_bound(self): s=np.array([-100,-1,0,1,100.]) self.assertGreaterEqual(softplus_bound(s,np.ones(5,bool)),(s>=0).sum()) def test_cp_zero_and_all(self): self.assertAlmostEqual(cp_upper(0,100,.05),1-.05**.01) self.assertEqual(cp_upper(100,100,.05),1.) def test_small_calibration_abstains(self): r=calibrate(np.zeros((80,101),int),np.linspace(0,1,101),64,alpha=.01,risk='any') self.assertTrue(r['reject_all']) def test_large_zero_calibration_certifies_any(self): r=calibrate(np.zeros((1000,2),int),[.1,.9],64,alpha=.01,risk='any') self.assertFalse(r['reject_all']);self.assertEqual(r['threshold'],.1) def test_count_and_any_distinct(self): counts=np.zeros((1000,2),int) self.assertTrue(calibrate(counts,[.1,.9],64,.01,risk='count')['reject_all']) self.assertFalse(calibrate(counts,[.1,.9],64,.01,risk='any')['reject_all']) def test_invalid_calibration(self): for x in (np.array([[65]]),np.array([[.5]]),np.array([[np.nan]])): with self.assertRaises(ValueError):calibrate(x,[.5],64) def test_fixed_sequence_zero_boundary(self): for n,expected in ((298,True),(299,False)): r=calibrate(np.zeros((n,101),int),np.linspace(0,1,101),64,.01,risk='any',method='fixed_sequence') self.assertEqual(r['reject_all'],expected) self.assertEqual(r['threshold'],0.) def test_fixed_sequence_stops_without_restart(self): # Deliberately nonmonotone table: later passing tests must not be inspected. x=np.zeros((1000,3),int);x[:,1]=1 r=calibrate(x,[.1,.5,.9],64,.01,risk='any',method='fixed_sequence') self.assertEqual(r['threshold'],.9) self.assertEqual([v['threshold'] for v in r['tested']],[.9,.5]) def test_fixed_sequence_count_zero_boundary(self): n=math.ceil(math.log(.05)/math.log1p(-.01/64)) for size,expected in ((n-1,True),(n,False)): r=calibrate(np.zeros((size,1),int),[.5],64,.01,method='fixed_sequence') self.assertEqual(r['reject_all'],expected) def test_fixed_sequence_support_endpoints(self): x=np.array([[0],[64]]) self.assertTrue(calibrate(x,[.5],64,0,method='fixed_sequence')['reject_all']) self.assertFalse(calibrate(x,[.5],64,64,method='fixed_sequence')['reject_all']) def test_pool_cap_and_stability(self): p=np.zeros((5,5));p[0,0]=.9;p[4,4]=.8 m,x=candidate_pool(p,.1,1);self.assertEqual(len(m),1);self.assertTrue(m[0][0,0]) def test_metrics_one_to_one(self): g=np.zeros((9,9),bool);g[4,4]=1 a=np.zeros_like(g);a[4,3]=1;b=np.zeros_like(g);b[4,5]=1 r=image_stats([a,b],g);self.assertEqual((r['tp'],r['fp'],r['fn']),(1,1,0)) def test_empty_image_conventions(self): r=aggregate([image_stats([],np.zeros((4,4),bool))]) self.assertEqual(r['nIoU'],1.);self.assertIsNone(r['Pd']);self.assertEqual(r['FP_per_image'],0.) def test_path_matches_enumeration(self): nodes=[[1.,2.],[3.,0.],[.5,2.]];cost=[np.array([[0.,2.],[1.,0.]]),np.array([[0.,1.],[3.,0.]])] best,path=best_path(nodes,cost) scores=[(sum(nodes[t][v] for t,v in enumerate(p))-sum(cost[t][p[t],p[t+1]] for t in range(2)),p) for p in itertools.product(range(2),repeat=3)] self.assertAlmostEqual(best,max(s[0] for s in scores));self.assertEqual(len(path),3) def test_disconnected_path(self): self.assertEqual(best_path([[1.],[2.]],[np.array([[np.inf]])]),(-math.inf,[])) def test_binary_penalty_exactness(self): w=[1.,2.,3.];edges=[(0,1),(1,2)];lam=4. values=[(sum(w[i]*b[i] for i in range(3))-lam*sum(b[i]*b[j] for i,j in edges),b) for b in itertools.product([0,1],repeat=3)] value,b=max(values);self.assertFalse(any(b[i] and b[j] for i,j in edges));self.assertEqual(value,exact_mwis(w,edges)[0])