File size: 4,179 Bytes
9d901ad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Tests for DeepPTR synthetic data generation and metrics."""

import numpy as np
import pytest

from scptr.deep.synthetic import generate_kinetic_data, gamma_recovery, ci_coverage, latent_recovery


class TestGenerateKineticData:
    def test_shapes(self):
        adata, truth = generate_kinetic_data(n_cells=100, n_genes=30, seed=0)
        assert adata.n_obs == 100
        assert adata.n_vars == 30
        assert adata.layers["spliced"].shape == (100, 30)
        assert adata.layers["unspliced"].shape == (100, 30)

    def test_truth_keys(self):
        _, truth = generate_kinetic_data(n_cells=50, n_genes=20)
        for key in ("alpha", "gamma", "beta", "z_T", "z_PT"):
            assert key in truth

    def test_truth_shapes(self):
        adata, truth = generate_kinetic_data(n_cells=50, n_genes=20)
        assert truth["alpha"].shape == (50, 20)
        assert truth["gamma"].shape == (50, 20)
        assert truth["beta"].shape == (20,)
        assert truth["z_T"].shape[0] == 50
        assert truth["z_PT"].shape[0] == 50

    def test_non_negative_counts(self):
        adata, _ = generate_kinetic_data(n_cells=200, n_genes=50)
        assert (adata.layers["spliced"] >= 0).all()
        assert (adata.layers["unspliced"] >= 0).all()

    def test_cell_types(self):
        adata, _ = generate_kinetic_data(n_cells=100, n_cell_types=4)
        assert "cell_type" in adata.obs.columns
        assert adata.obs["cell_type"].nunique() <= 4

    def test_sparsity(self):
        adata_sparse, _ = generate_kinetic_data(n_cells=500, n_genes=100, sparsity=0.5)
        adata_dense, _ = generate_kinetic_data(n_cells=500, n_genes=100, sparsity=0.0)
        frac_zero_sparse = (adata_sparse.layers["spliced"] == 0).mean()
        frac_zero_dense = (adata_dense.layers["spliced"] == 0).mean()
        assert frac_zero_sparse > frac_zero_dense

    def test_reproducible(self):
        adata1, t1 = generate_kinetic_data(seed=42)
        adata2, t2 = generate_kinetic_data(seed=42)
        np.testing.assert_array_equal(
            adata1.layers["spliced"], adata2.layers["spliced"]
        )
        np.testing.assert_array_equal(t1["gamma"], t2["gamma"])


class TestGammaRecovery:
    def test_perfect_recovery(self):
        rng = np.random.RandomState(0)
        gamma = rng.rand(100, 20).astype(np.float32)
        r = gamma_recovery(gamma, gamma, per_gene=True)
        assert r > 0.99

    def test_random_is_low(self):
        rng = np.random.RandomState(0)
        g1 = rng.rand(100, 20).astype(np.float32)
        g2 = rng.rand(100, 20).astype(np.float32)
        r = gamma_recovery(g1, g2, per_gene=True)
        assert abs(r) < 0.3

    def test_global_mode(self):
        rng = np.random.RandomState(0)
        gamma = rng.rand(100, 20).astype(np.float32)
        r = gamma_recovery(gamma, gamma, per_gene=False)
        assert r > 0.99


class TestCICoverage:
    def test_perfect_coverage(self):
        rng = np.random.RandomState(0)
        gamma = rng.rand(100, 20).astype(np.float32)
        # Huge variance → everything covered
        cov = ci_coverage(gamma, gamma, np.ones_like(gamma) * 100.0)
        assert cov > 0.99

    def test_zero_variance_coverage(self):
        rng = np.random.RandomState(0)
        gamma_true = rng.rand(100, 20).astype(np.float32)
        gamma_pred = gamma_true + 1.0  # shifted
        # Tiny variance → nothing covered
        cov = ci_coverage(gamma_true, gamma_pred, np.ones_like(gamma_true) * 1e-10)
        assert cov < 0.05

    def test_returns_fraction(self):
        rng = np.random.RandomState(0)
        gamma = rng.rand(50, 10).astype(np.float32)
        cov = ci_coverage(gamma, gamma, np.ones_like(gamma) * 0.1)
        assert 0.0 <= cov <= 1.0


class TestLatentRecovery:
    def test_perfect_recovery(self):
        rng = np.random.RandomState(0)
        z = rng.randn(100, 5).astype(np.float32)
        r = latent_recovery(z, z)
        assert r > 0.99

    def test_random_is_lower(self):
        rng = np.random.RandomState(0)
        z1 = rng.randn(100, 5).astype(np.float32)
        z2 = rng.randn(100, 5).astype(np.float32)
        r = latent_recovery(z1, z2)
        assert r < 0.5