File size: 7,462 Bytes
9d6c005 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | 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()
|