cfREF / tests /test_cfref.py
qiongli0705's picture
Upload 10 files
703e278 verified
Raw History Blame Contribute Delete
3.27 kB
import tempfile
import unittest
from pathlib import Path
import numpy as np
import torch
from sklearn.preprocessing import StandardScaler
from cfref import CFREF
from cfref._core import train_cfref
class TestCFREF(unittest.TestCase):
@classmethod
def setUpClass(cls):
torch.set_num_threads(1)
rng = np.random.RandomState(42)
cls.X = rng.normal(size=(48, 6))
cls.y = np.tile([0]*16+[1]*8, 2)
cls.c = np.repeat(['source_A','source_B'], 24)
cls.names = ['bin_'+str(i) for i in range(6)]
cls.R = rng.normal(size=(5,6)); cls.Q = rng.normal(size=(9,6))
cls.kw = dict(hidden_dim=8, embedding_dim=4, episodes=12,
reference_range=(3,5), threshold_references=3, threshold_draws=4)
cls.model = CFREF(**cls.kw).fit(cls.X,cls.y,cls.c,feature_names=cls.names)
def test_original_core_parity(self):
Z = StandardScaler().fit_transform(self.X).astype(np.float32)
net = train_cfref(Z,self.y,self.c,n_ref_range=(3,5),episodes=12,d_h=8,d_e=4)
for k,v in net.state_dict().items():
np.testing.assert_array_equal(v.detach().numpy(),self.model.net_.state_dict()[k].numpy())
rng=np.random.RandomState(0); out=[]; Xt=torch.tensor(Z)
with torch.no_grad():
for c in np.unique(self.c):
ctrl=np.where((self.c==c)&(self.y==0))[0]
for _ in range(4):
ref=rng.choice(ctrl,3,replace=False)
out.append(net(Xt[np.setdiff1d(ctrl,ref)],Xt[ref]).numpy())
self.assertEqual(self.model.threshold_,float(np.quantile(np.concatenate(out),.95)))
def test_save_load(self):
with tempfile.TemporaryDirectory() as d:
p=Path(d)/'model.cfref';self.model.save(p); m=CFREF.load(p)
np.testing.assert_array_equal(m.decision_function(self.Q,self.R,feature_names=self.names),self.model.decision_function(self.Q,self.R,feature_names=self.names))
self.assertEqual(m.threshold_,self.model.threshold_)
def test_permutation_invariance(self):
np.testing.assert_allclose(self.model.decision_function(self.Q,self.R,feature_names=self.names),self.model.decision_function(self.Q,self.R[::-1],feature_names=self.names),rtol=1e-5,atol=1e-6)
def test_feature_mismatch(self):
with self.assertRaises(ValueError):self.model.predict(self.Q,self.R,feature_names=self.names[::-1])
def test_small_reference(self):
with self.assertRaises(ValueError):self.model.predict(self.Q,self.R[:1],feature_names=self.names)
def test_missing_threshold_contributors(self):
with self.assertRaisesRegex(ValueError,'No source cohort'):
CFREF().fit(self.X,self.y,self.c,feature_names=self.names)
def test_invalid_values(self):
X=self.X.copy();X[0,0]=np.nan
with self.assertRaises(ValueError):CFREF(**self.kw).fit(X,self.y,self.c,feature_names=self.names)
def test_scaling_is_fixed_at_inference(self):
before=self.model.mean_.copy()
self.model.predict(self.Q+10,self.R+10,feature_names=self.names)
np.testing.assert_array_equal(before,self.model.mean_)
np.testing.assert_allclose(before,self.X.mean(axis=0))
if __name__=='__main__':unittest.main()