Skip to content

Add comprehensive unit tests for animl/reid/#275

Merged
tkswanson merged 2 commits into
devfrom
copilot/add-tests-for-distance-module
Apr 24, 2026
Merged

Add comprehensive unit tests for animl/reid/#275
tkswanson merged 2 commits into
devfrom
copilot/add-tests-for-distance-module

Conversation

Copy link
Copy Markdown

Copilot AI commented Apr 23, 2026

No unit tests existed for src/animl/reid/. This adds tests/test_reid.py with full coverage of the distance, miewid, and inference modules, following the same unittest.TestCase conventions as test_classification.py.

Test classes

  • TestRemoveDiagonal — shape correctness, non-square ValueError, diagonal removal
  • TestEuclideanSquaredDistance — output shape, self-distance=0, known values (3²+4²=25), non-negativity
  • TestCosineDistance — identical/orthogonal/opposite vectors, range clamped to [0, 2]
  • TestComputeDistanceMatrix — numpy and torch inputs, (m,n) shapes, unknown metric raises ValueError, 1-D input raises AssertionError, self-diagonal ≈ 0
  • TestComputeBatchedDistanceMatrix — shape, batched vs. unbatched parity, torch tensor input, batch_size > n
  • TestL2Norm — unit-norm output per row, shape invariance
  • TestGeM — forward shape (b, c, 1, 1), repr contains p= and eps=
  • TestMiewIdNetpretrained=False (no network I/O); extract_feat returns 2-D tensor with correct batch dim; forward output equals extract_feat
  • TestLoadMiew — skipped via unittest.SkipTest when neither models/miewid.pt nor models/miewid.onnx is present; validates .framework attribute and eval mode
  • TestExtractMiewEmbeddings — skipped when model or ground-truth CSV is absent; checks ndarray return, row count, positive embedding dim, finiteness, and ValueError on missing file_col

38 tests total; 2 model-dependent classes skip gracefully in environments without downloaded weights.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • dns.google
    • Triggering command: /usr/bin/python python -c import numpy; import torch; import animl (dns block)
    • Triggering command: /usr/bin/python python -m unittest discover -s tests -p test_reid.py -v (dns block)
  • one.one.one.one
    • Triggering command: /usr/bin/python python -c import numpy; import torch; import animl (dns block)
    • Triggering command: /usr/bin/python python -m unittest discover -s tests -p test_reid.py -v (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

Create tests/test_reid.py on the dev branch with comprehensive unit tests for src/animl/reid/. Follow the exact same style as tests/test_classification.pyunittest.TestCase subclasses, setUpClass with unittest.SkipTest for model-dependent classes, and unittest.main() at the bottom.


Source modules to test

src/animl/reid/distance.py

  • remove_diagonal(A) — removes diagonal from a square torch.Tensor; raises ValueError on non-square
  • euclidean_squared_distance(input1, input2) — returns torch.Tensor of shape (m, n)
  • cosine_distance(input1, input2) — returns torch.Tensor of shape (m, n)
  • compute_distance_matrix(input1, input2, metric) — wrapper; accepts numpy or torch; returns np.ndarray; raises ValueError on unknown metric or wrong dims
  • compute_batched_distance_matrix(input1, input2, metric, batch_size) — batched version using scipy.spatial.distance.cdist; returns np.ndarray

src/animl/reid/inference.py

  • load_miew(file_path, device) — loads .pt (torch) or .onnx model; sets .framework attr
  • extract_miew_embeddings(miew_model, manifest, file_col, batch_size, num_workers, device) — returns np.ndarray; raises ValueError when file_col missing

src/animl/reid/miewid.py

  • l2_norm(input, axis) — returns unit-norm tensor
  • GeM — pooling module, forward produces a tensor
  • MiewIdNet — instantiates on CPU with pretrained=False; extract_feat returns correct embedding shape

Test classes to implement

TestRemoveDiagonal

No model needed. Use torch.Tensor inputs.

class TestRemoveDiagonal(unittest.TestCase):

    def test_output_shape_3x3(self):
        A = torch.arange(9, dtype=torch.float).reshape(3, 3)
        result = remove_diagonal(A)
        self.assertEqual(result.shape, (3, 2))

    def test_output_shape_4x4(self):
        A = torch.ones(4, 4)
        result = remove_diagonal(A)
        self.assertEqual(result.shape, (4, 3))

    def test_non_square_raises(self):
        A = torch.ones(2, 3)
        with self.assertRaises(ValueError):
            remove_diagonal(A)

    def test_diagonal_values_removed(self):
        # identity matrix — diagonal is 1, off-diagonal is 0
        A = torch.eye(3)
        result = remove_diagonal(A)
        self.assertTrue((result == 0).all())

    def test_2x2_edge_case(self):
        A = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
        result = remove_diagonal(A)
        self.assertEqual(result.shape, (2, 1))

TestEuclideanSquaredDistance

No model needed.

class TestEuclideanSquaredDistance(unittest.TestCase):

    def test_output_shape(self):
        a = torch.randn(3, 4)
        b = torch.randn(5, 4)
        result = euclidean_squared_distance(a, b)
        self.assertEqual(result.shape, (3, 5))

    def test_self_distance_is_zero(self):
        a = torch.tensor([[1.0, 2.0, 3.0]])
        result = euclidean_squared_distance(a, a)
        self.assertAlmostEqual(result[0, 0].item(), 0.0, places=5)

    def test_known_values(self):
        a = torch.tensor([[0.0, 0.0]])
        b = torch.tensor([[3.0, 4.0]])
        result = euclidean_squared_distance(a, b)
        self.assertAlmostEqual(result[0, 0].item(), 25.0, places=5)

    def test_non_negative(self):
        a = torch.randn(4, 3)
        result = euclidean_squared_distance(a, a)
        self.assertTrue((result >= -1e-5).all())

    def test_square_output_for_same_inputs(self):
        a = torch.randn(5, 8)
        result = euclidean_squared_distance(a, a)
        self.assertEqual(result.shape, (5, 5))

TestCosineDistance

No model needed.

class TestCosineDistance(unittest.TestCase):

    def test_identical_vectors_distance_is_zero(self):
        a = torch.tensor([[1.0, 0.0, 0.0]])
        result = cosine_distance(a, a)
        self.assertAlmostEqual(result[0, 0].item(), 0.0, places=5)

    def test_orthogonal_vectors_distance_is_one(self):
        a = torch.tensor([[1.0, 0.0]])
        b = torch.tensor([[0.0, 1.0]])
        result = cosine_distance(a, b)
        self.assertAlmostEqual(result[0, 0].item(), 1.0, places=5)

    def test_opposite_vectors_distance_is_two(self):
        a = torch.tensor([[1.0, 0.0]])
        b = torch.tensor([[-1.0, 0.0]])
        result = cosine_distance(a, b)
        self.assertAlmostEqual(result[0, 0].item(), 2.0, places=5)

    def test_output_shape(self):
        a = torch.randn(3, 4)
        b = torch.randn(2, 4)
        result = cosine_distance(a, b)
        self.assertEqual(result.shape, (3, 2))

    def test_values_in_range(self):
        a = torch.randn(5, 8)
        b = torch.randn(4, 8)
        result = cosine_distance(a, b)
        self.assertTrue((result >= -1e-5).all())
        self.assertTrue((result <= 2 + 1e-5).all())

TestComputeDistanceMatrix

No model needed.

class TestComputeDistanceMatrix(unittest.TestCase):

    def test_euclidean_returns_numpy(self):
        a = np.random.randn(3, 4).a...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

Copilot AI changed the title [WIP] Add comprehensive unit tests for reid distance functions Add comprehensive unit tests for animl/reid/ Apr 23, 2026
Copilot AI requested a review from tkswanson April 23, 2026 23:21
@tkswanson tkswanson marked this pull request as ready for review April 24, 2026 17:42
@tkswanson tkswanson merged commit 1006205 into dev Apr 24, 2026
@tkswanson tkswanson deleted the copilot/add-tests-for-distance-module branch April 24, 2026 17:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants