File size: 4,051 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
"""Meaningful algebraic/counterexample checks, independent of the benchmark data."""
import unittest
import numpy as np
from aureole_core import *


class TheoryChecks(unittest.TestCase):
    def setUp(self):
        self.rng=np.random.default_rng(20260919)
        b=self.rng.normal(size=(7,7)); self.p=b@b.T+.2*np.eye(7)
        c=self.rng.normal(size=(5,7)); self.w=c.T@c
        self.h=self.rng.normal(size=7)

    def test_value_matches_direct_risk_reduction(self):
        _,p2=observe(np.zeros(7),self.p,self.h,2.,.6)
        self.assertAlmostEqual(risk(self.p,self.w)-risk(p2,self.w),query_value(self.p,self.w,self.h,.6),places=10)

    def test_update_psd_and_information_order(self):
        _,p2=observe(np.zeros(7),self.p,self.h,2.,.6)
        self.assertGreater(np.linalg.eigvalsh(p2).min(),0)
        self.assertGreater(np.linalg.eigvalsh(self.p-p2).min(),-1e-10)

    def test_batch_equals_sequential_independent_samples(self):
        h=self.rng.normal(size=(3,7)); r=np.diag([.2,.5,.7]); p=self.p.copy()
        for hi,ri in zip(h,np.diag(r)): _,p=observe(np.zeros(7),p,hi,0.,ri)
        np.testing.assert_allclose(p,batch_covariance(self.p,h,r),atol=1e-10)

    def test_task_additivity(self):
        w1=np.diag([1.,2.,3.,0.,0.,0.,0.]); w2=self.w
        self.assertAlmostEqual(query_value(self.p,w1+w2,self.h,.3),query_value(self.p,w1,self.h,.3)+query_value(self.p,w2,self.h,.3),places=10)

    def test_future_metric_matches_explicit_loss(self):
        a=self.rng.normal(size=(7,7))*.1; cs=[self.rng.normal(size=(4,7)) for _ in range(6)]; weights=.9**np.arange(6)
        w=future_metric(a,cs,weights); x=self.rng.normal(size=7); total=0.; z=x.copy()
        for c,weight in zip(cs,weights): total+=weight*np.linalg.norm(c@z)**2; z=a@z
        self.assertAlmostEqual(total,x@w@x,places=10)

    def test_transform_tail(self):
        for rank in [0,1,4,7]:
            u,e,p=transform_coding(self.p,self.w,rank)
            self.assertAlmostEqual(risk(p,self.w),sum(e[rank:]),places=9)

    def test_nuisance_query_expands_basis(self):
        u=closed_observation_basis([np.eye(2)],[[1,0],[1,1]])
        self.assertEqual(u.shape[1],2)

    def test_dynamic_closure(self):
        a=np.array([[.9,1.,0.],[0,.8,0.],[0,0,.7]])
        u=closed_observation_basis([a],[[1,0,0]])
        self.assertEqual(u.shape[1],2)
        np.testing.assert_allclose((np.eye(3)-u@u.T)@a.T@u,0.,atol=1e-10)

    def test_projection_preserves_linear_future_channels(self):
        a=np.array([[.9,1.,0.],[0,.8,0.],[0,0,.7]]); c=np.array([[1.,0.,0.]])
        u=closed_observation_basis([a],c); x=self.rng.normal(size=3); z=u.T@x
        for _ in range(15):
            np.testing.assert_allclose(c@x,c@u@z,atol=1e-9)
            x=a@x; z=u.T@a@u@z

    def test_sampling_not_generally_submodular(self):
        p=np.eye(2); w=np.diag([1.,0.]); h1=np.array([1.,1.]); h2=np.array([0.,1.])
        _,p1=observe(np.zeros(2),p,h1,0,.1)
        self.assertEqual(query_value(p,w,h2,.1),0.)
        self.assertGreater(query_value(p1,w,h2,.1),.3)

    def test_optical_area_mixture_passive_reciprocal(self):
        ops=[]
        for _ in range(4):
            b=self.rng.random((6,6)); b=sym(b); b/=b.sum(0).max()*1.1; ops.append(b)
        k=area_mixture(np.array(ops),[.1,.2,.3,.4])
        self.assertGreaterEqual(k.min(),0.); self.assertLessEqual(k.sum(0).max(),1.)
        np.testing.assert_allclose(k,k.T)

    def test_measurement_information_does_not_increase_while_absent(self):
        q=np.diag(np.linspace(.01,.07,7)); p2=self.p+500*q
        self.assertGreaterEqual(np.linalg.eigvalsh(p2-self.p).min(),0.)

    def test_forgetting_has_nonnegative_bayes_risk(self):
        _,posterior=observe(np.zeros(7),self.p,self.h,0.,.1)
        self.assertGreaterEqual(risk(self.p-posterior,self.w),-1e-10)

    def test_impossible_future_bit(self):
        # E[(B-a)^2] = 1+a^2, uniquely minimized at a=0.
        for a in [-2.,-.5,0.,.5,2.]: self.assertAlmostEqual(.5*(1-a)**2+.5*(-1-a)**2,1+a*a)


if __name__=="__main__": unittest.main(verbosity=2)