| import unittest,itertools,tempfile |
| from pathlib import Path |
| import numpy as np |
| from aureole.certificates import CertificateMemory,visibility_certificate,enclosure |
| from aureole.innovation import prepare,eliminate,exact_risk_two,covariance |
| from aureole.renderer import Scene,receiver_grid,light_grid |
|
|
|
|
| class InnovationTests(unittest.TestCase): |
| def test_covariance_contraction_all_readouts(self): |
| rng=np.random.default_rng(121) |
| for _ in range(200): |
| q=rng.dirichlet(np.ones(9));r=rng.normal(size=(9,5)) |
| a=np.arange(3);u=np.arange(3,9);mass=q[a].sum() |
| old=covariance(r,q) |
| new=covariance(r[u],q[u]/(1-mass)) |
| self.assertGreaterEqual(np.linalg.eigvalsh((1-mass)*old-new).min(),-1e-10) |
|
|
| def test_exact_two_draw_enumeration(self): |
| rng=np.random.default_rng(12) |
| for k in (3,4,6): |
| q=rng.dirichlet(np.ones(k));f=rng.uniform(size=(k,3));h=rng.normal(size=(k,3)) |
| outcomes=[];weights=[] |
| for a,b in itertools.permutations(range(k),2): |
| y1=h.sum(0)+(f[a]-h[a])/q[a] |
| h2=h.copy();h2[a]=f[a] |
| y2=h2.sum(0)+(f[b]-h2[b])/(q[b]/(1-q[a])) |
| outcomes.append((y1+y2)/2);weights.append(q[a]*q[b]/(1-q[a])) |
| outcomes=np.array(outcomes);weights=np.array(weights) |
| np.testing.assert_allclose(weights@outcomes,f.sum(0),atol=1e-12) |
| risk=np.sum(weights[:,None]*(outcomes-f.sum(0))**2)/3 |
| self.assertAlmostEqual(risk,exact_risk_two(f[None],h[None],q[None])[0],places=11) |
|
|
| def test_exact_completion_fixed_budget(self): |
| f=np.arange(9,dtype=float).reshape(1,3,3) |
| used=[] |
| def oracle(rows,j):used.extend(j.tolist());return f[rows,j] |
| y,_=eliminate(np.zeros_like(f),np.full((1,3),1/3),oracle,3,np.random.default_rng(3)) |
| np.testing.assert_array_equal(y,f.sum(1));self.assertEqual(len(set(used)),3) |
|
|
| def test_no_rays_on_complete_state(self): |
| h=np.ones((4,3,2)) |
| def oracle(*args):raise AssertionError('Should not trace') |
| y,steps=eliminate(h,np.zeros((4,3)),oracle,2,np.random.default_rng(3)) |
| np.testing.assert_array_equal(y,h.sum(1));self.assertEqual(steps,[]) |
|
|
| def test_false_zero_support_rejected_by_audit(self): |
| with self.assertRaises(ValueError):exact_risk_two(np.ones((1,3,1)),np.zeros((1,3,1)),np.array([[.5,.5,0]])) |
|
|
| def test_upper_uniform_endpoint_never_selects_zero_support(self): |
| class UpperRng: |
| def random(self,n):return np.full(n,np.nextafter(1.0,0.0)) |
| q=np.zeros((1000,7));q[:,:5]=np.random.default_rng(41).dirichlet(np.ones(5),size=1000) |
| def oracle(rows,j): |
| self.assertTrue(np.all(j<5));return np.ones((len(rows),1)) |
| result,steps=eliminate(np.zeros((1000,7,1)),q,oracle,2,UpperRng()) |
| self.assertTrue(np.isfinite(result).all()) |
| self.assertTrue(np.all(steps[0][1]!=steps[1][1])) |
|
|
| def test_known_only_control_and_support(self): |
| b=np.ones((1,4,3));v=np.array([[0.,1.,0.,0.]]) |
| h,q=prepare(b,np.full((1,4),.6),v,np.array([[True,True,False,False]]),np.ones((1,4))) |
| np.testing.assert_array_equal(q,[[0,0,.5,.5]]) |
| np.testing.assert_allclose(h[0,:,0],[0,1,.6,.6]) |
|
|
| def test_invalid_budget_and_proposal(self): |
| with self.assertRaises(ValueError):eliminate(np.ones((1,3,1)),np.full((1,3),1/3),lambda *x:None,True,np.random.default_rng()) |
| with self.assertRaises(ValueError):eliminate(np.ones((1,3,1)),np.array([[0,.1,.1]]),lambda *x:None,1,np.random.default_rng()) |
|
|
| def test_float_oracle_matches_independent_quadratic(self): |
| rng=np.random.default_rng(987) |
| for sid in range(800,820): |
| scene=Scene.create(sid) |
| p=rng.uniform([-1,-1,0],[1,1,0],(200,3));l=rng.uniform([-.9,-.9,2.2],[.9,.9,2.2],(200,3)) |
| v,_=visibility_certificate(scene,p,l) |
| np.testing.assert_array_equal(v,scene.visibility(p,l)) |
|
|
| def test_moving_and_spatial_certificates_no_false_acceptance(self): |
| rng=np.random.default_rng(982) |
| for sid in range(820,830): |
| scene=Scene.create(sid);p=receiver_grid(8,12);l=light_grid(4) |
| mem=CertificateMemory(p,l,scene.spheres) |
| ids=np.repeat(np.arange(len(p)),len(l));j=np.tile(np.arange(len(l)),len(p)) |
| v,m=visibility_certificate(scene,p[ids],l[j]);mem.commit(ids,j,v,m) |
| for _ in range(12): |
| g=scene.spheres.copy();g[:,:3]+=rng.uniform(-.035,.035,(3,3));g[:,3]*=rng.uniform(.96,1.04,3) |
| moved=Scene(sid,g);mem.begin_geometry(g) |
| query=p+rng.uniform(-.012,.012,p.shape);query[:,2]=0 |
| values,known=mem.lookup(np.arange(len(p)),query) |
| truth=moved.visibility(query[:,None,:],l[None,:,:]) |
| self.assertTrue(np.all(values[known]==truth[known])) |
|
|
| def test_unreported_motion_is_not_protected(self): |
| scene=Scene.create(833);p=receiver_grid(16,20);l=light_grid(6) |
| mem=CertificateMemory(p,l,scene.spheres) |
| ids=np.repeat(np.arange(len(p)),len(l));j=np.tile(np.arange(len(l)),len(p)) |
| v,m=visibility_certificate(scene,p[ids],l[j]);mem.commit(ids,j,v,m) |
| values,known=mem.lookup(np.arange(len(p))) |
| changed=scene.changed().visibility(p[:,None,:],l[None,:,:]) |
| self.assertGreater(np.count_nonzero(known & (values!=changed)),0) |
|
|
| def test_epoch_resets_while_margin_can_survive(self): |
| s=Scene.create(20);p=receiver_grid(4,5);l=light_grid(2) |
| a=CertificateMemory(p,l,s.spheres);b=CertificateMemory(p,l,s.spheres,mode='epoch') |
| ids=np.arange(len(p));j=np.zeros(len(p),int);v,m=visibility_certificate(s,p,l[j]) |
| for mem in (a,b):mem.commit(ids,j,v,m) |
| g=s.spheres.copy();g[0,0]+=1e-7 |
| for mem in (a,b):mem.begin_geometry(g) |
| self.assertGreater(a.lookup(ids)[1].sum(),0);self.assertEqual(b.lookup(ids)[1].sum(),0) |
|
|
| def test_enclosure_covers_all_binary_completions(self): |
| b=np.arange(1,10,dtype=float).reshape(1,3,3);v=np.array([[1.,0.,0.]]);known=np.array([[True,False,False]]) |
| lo,hi=enclosure(b,v,known) |
| for a,c in itertools.product((0,1),repeat=2): |
| y=(b*np.array([1,a,c])[None,:,None]).sum(1) |
| self.assertTrue((y>=lo).all() and (y<=hi).all()) |
|
|
| def test_roundtrip_and_namespace(self): |
| s=Scene.create(31);p=receiver_grid(3,4);l=light_grid(2) |
| a=CertificateMemory(p,l,s.spheres,'alpha');ids=np.arange(len(p));j=np.zeros(len(p),int) |
| v,m=visibility_certificate(s,p,l[j]);a.commit(ids,j,v,m) |
| with tempfile.TemporaryDirectory() as d: |
| path=Path(d)/'memory.npz';a.save(path);b=CertificateMemory.load(path,'alpha') |
| np.testing.assert_array_equal(a.lookup(ids)[1],b.lookup(ids)[1]) |
| with self.assertRaises(ValueError):CertificateMemory.load(path,'beta') |
|
|
| def test_motion_budget_is_conservative_on_return(self): |
| s=Scene.create(12);a=CertificateMemory(receiver_grid(2,2),light_grid(2),s.spheres) |
| g=s.spheres.copy();g[0,0]+=.2;a.begin_geometry(g);a.begin_geometry(s.spheres) |
| self.assertGreaterEqual(a.budget,.4-1e-14) |
|
|
| def test_readout_cross_covariance_is_psd(self): |
| rng=np.random.default_rng(22);r=rng.normal(size=(7,12));q=rng.dirichlet(np.ones(7)) |
| self.assertGreaterEqual(np.linalg.eigvalsh(covariance(r,q)).min(),-1e-10) |
|
|
|
|
| if __name__=='__main__':unittest.main() |
|
|