Completed
Push — main ( bd3202...72b597 )
by Yunguan
27s queued 13s
created

test.unit.test_loss_label   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 233
Duplicated Lines 16.74 %

Importance

Changes 0
Metric Value
wmc 19
eloc 158
dl 39
loc 233
rs 10
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A TestDiceScore.y_true() 0 3 1
A TestCrossEntropy.test_get_config() 0 11 1
A TestCrossEntropy.y_pred() 0 3 1
A TestJaccardIndex.test_get_config() 0 10 1
A TestJaccardIndex.y_true() 0 3 1
A TestDiceScore.test_call() 20 20 1
A TestCrossEntropy.test_call() 0 16 1
A TestJaccardIndex.test_call() 19 19 1
A TestCrossEntropy.y_true() 0 3 1
A TestJaccardIndex.y_pred() 0 3 1
A TestMultiScaleLoss.test_call() 0 4 2
A TestDiceScore.test_get_config() 0 11 1
A TestMultiScaleLoss.test_get_config() 0 10 1
A TestDiceScore.y_pred() 0 3 1

4 Functions

Rating   Name   Duplication   Size   Complexity  
A test_compute_centroid() 0 18 1
A test_compute_centroid_d() 0 17 1
A test_foreground_prop_simple() 0 15 1
A test_foreground_prop_binary() 0 13 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
# coding=utf-8
2
3
"""
4
Tests for deepreg/model/loss/label.py in
5
pytest style
6
"""
7
8
from test.unit.util import is_equal_tf
9
10
import numpy as np
11
import pytest
12
import tensorflow as tf
13
14
import deepreg.loss.label as label
15
16
17
class TestMultiScaleLoss:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
18
    def test_call(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
19
        loss = label.MultiScaleLoss()
20
        with pytest.raises(NotImplementedError):
21
            loss.call(0, 0)
22
23
    def test_get_config(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
24
        loss = label.MultiScaleLoss()
25
        got = loss.get_config()
26
        expected = dict(
27
            scales=None,
28
            kernel="gaussian",
29
            reduction=tf.keras.losses.Reduction.SUM,
30
            name="MultiScaleLoss",
31
        )
32
        assert got == expected
33
34
35
class TestDiceScore:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
36
    shape = (3, 3, 3, 3)
37
38
    @pytest.fixture()
39
    def y_true(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
40
        return np.ones(shape=self.shape) * 0.6
41
42
    @pytest.fixture()
43
    def y_pred(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
44
        return np.ones(shape=self.shape) * 0.3
45
46 View Code Duplication
    @pytest.mark.parametrize(
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
47
        "binary,neg_weight,scales,expected",
48
        [
49
            (True, 0.0, None, 0.0),
50
            (False, 0.0, None, 0.4),
51
            (False, 0.2, None, 0.4 / 0.94),
52
            (False, 0.2, [0, 0], 0.4 / 0.94),
53
            (False, 0.2, [0, 1], 0.46030036),
54
        ],
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
55
    )
56
    def test_call(self, y_true, y_pred, binary, neg_weight, scales, expected):
57
        expected = np.array([expected] * self.shape[0])  # call returns (batch, )
58
        got = label.DiceScore(binary=binary, neg_weight=neg_weight, scales=scales).call(
59
            y_true=y_true, y_pred=y_pred
60
        )
61
        assert is_equal_tf(got, expected)
62
        got = label.DiceLoss(binary=binary, neg_weight=neg_weight, scales=scales).call(
63
            y_true=y_true, y_pred=y_pred
64
        )
65
        assert is_equal_tf(got, -expected)
66
67
    def test_get_config(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
68
        got = label.DiceScore().get_config()
69
        expected = dict(
70
            binary=False,
71
            neg_weight=0.0,
72
            scales=None,
73
            kernel="gaussian",
74
            reduction=tf.keras.losses.Reduction.SUM,
75
            name="DiceScore",
76
        )
77
        assert got == expected
78
79
80
class TestCrossEntropy:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
81
    shape = (3, 3, 3, 3)
82
83
    @pytest.fixture()
84
    def y_true(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
85
        return np.ones(shape=self.shape) * 0.6
86
87
    @pytest.fixture()
88
    def y_pred(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
89
        return np.ones(shape=self.shape) * 0.3
90
91
    @pytest.mark.parametrize(
92
        "binary,neg_weight,scales,expected",
93
        [
94
            (True, 0.0, None, -np.log(1.0e-7)),
95
            (False, 0.0, None, -0.6 * np.log(0.3)),
96
            (False, 0.2, None, -0.48 * np.log(0.3) - 0.08 * np.log(0.7)),
97
            (False, 0.2, [0, 0], -0.48 * np.log(0.3) - 0.08 * np.log(0.7)),
98
            (False, 0.2, [0, 1], 0.5239637),
99
        ],
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
100
    )
101
    def test_call(self, y_true, y_pred, binary, neg_weight, scales, expected):
102
        expected = np.array([expected] * self.shape[0])  # call returns (batch, )
103
        got = label.CrossEntropy(
104
            binary=binary, neg_weight=neg_weight, scales=scales
105
        ).call(y_true=y_true, y_pred=y_pred)
106
        assert is_equal_tf(got, expected)
107
108
    def test_get_config(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
109
        got = label.CrossEntropy().get_config()
110
        expected = dict(
111
            binary=False,
112
            neg_weight=0.0,
113
            scales=None,
114
            kernel="gaussian",
115
            reduction=tf.keras.losses.Reduction.SUM,
116
            name="CrossEntropy",
117
        )
118
        assert got == expected
119
120
121
class TestJaccardIndex:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
122
    shape = (3, 3, 3, 3)
123
124
    @pytest.fixture()
125
    def y_true(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
126
        return np.ones(shape=self.shape) * 0.6
127
128
    @pytest.fixture()
129
    def y_pred(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
130
        return np.ones(shape=self.shape) * 0.3
131
132 View Code Duplication
    @pytest.mark.parametrize(
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
133
        "binary,scales,expected",
134
        [
135
            (True, None, 0),
136
            (False, None, 0.25),
137
            (False, [0, 0], 0.25),
138
            (False, [0, 1], 0.17484076),
139
        ],
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
140
    )
141
    def test_call(self, y_true, y_pred, binary, scales, expected):
142
        expected = np.array([expected] * self.shape[0])  # call returns (batch, )
143
        got = label.JaccardIndex(binary=binary, scales=scales).call(
144
            y_true=y_true, y_pred=y_pred
145
        )
146
        assert is_equal_tf(got, expected)
147
        got = label.JaccardLoss(binary=binary, scales=scales).call(
148
            y_true=y_true, y_pred=y_pred
149
        )
150
        assert is_equal_tf(got, -expected)
151
152
    def test_get_config(self):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
153
        got = label.JaccardIndex().get_config()
154
        expected = dict(
155
            binary=False,
156
            scales=None,
157
            kernel="gaussian",
158
            reduction=tf.keras.losses.Reduction.SUM,
159
            name="JaccardIndex",
160
        )
161
        assert got == expected
162
163
164
def test_foreground_prop_binary():
165
    """
166
    Test foreground function with a
167
    tensor of zeros with some ones, asserting
168
    equal to known precomputed tensor.
169
    Testing with binary case.
170
    """
171
    array_eye = np.identity(3, dtype=np.float32)
172
    tensor_eye = np.zeros((3, 3, 3, 3), dtype=np.float32)
173
    tensor_eye[:, :, 0:3, 0:3] = array_eye
174
    expect = tf.convert_to_tensor([1.0 / 3, 1.0 / 3, 1.0 / 3], dtype=tf.float32)
175
    get = label.foreground_proportion(tensor_eye)
176
    assert is_equal_tf(get, expect)
177
178
179
def test_foreground_prop_simple():
180
    """
181
    Test foreground functions with a tensor
182
    of zeros with some ones and some values below
183
    one to assert the thresholding works.
184
    """
185
    array_eye = np.identity(3, dtype=np.float32)
186
    tensor_eye = np.zeros((3, 3, 3, 3), dtype=np.float32)
187
    tensor_eye[:, 0, :, :] = 0.4 * array_eye  # 0
188
    tensor_eye[:, 1, :, :] = array_eye
189
    tensor_eye[:, 2, :, :] = array_eye
190
    tensor_eye = tf.convert_to_tensor(tensor_eye, dtype=tf.float32)
191
    expect = [54 / (27 * 9), 54 / (27 * 9), 54 / (27 * 9)]
192
    get = label.foreground_proportion(tensor_eye)
193
    assert is_equal_tf(get, expect)
194
195
196
def test_compute_centroid():
197
    """
198
    Testing compute centroid function
199
    and comparing to expected values.
200
    """
201
    tensor_mask = np.zeros((3, 2, 2, 2))
202
    tensor_mask[0, :, :, :] = np.ones((2, 2, 2))
203
    tensor_mask = tf.constant(tensor_mask, dtype=tf.float32)
204
205
    tensor_grid = np.ones((2, 2, 2, 3))
206
    tensor_grid[:, :, :, 1] *= 2
207
    tensor_grid[:, :, :, 2] *= 3
208
    tensor_grid = tf.constant(tensor_grid, dtype=tf.float32)
209
210
    expected = np.ones((3, 3))  # use 1 because 0/0 ~= (0+eps)/(0+eps) = 1
211
    expected[0, :] = [1, 2, 3]
212
    got = label.compute_centroid(tensor_mask, tensor_grid)
213
    assert is_equal_tf(got, expected)
214
215
216
def test_compute_centroid_d():
217
    """
218
    Testing compute centroid distance between equal
219
    tensors returns 0s.
220
    """
221
    array_ones = np.ones((2, 2))
222
    tensor_mask = np.zeros((3, 2, 2, 2))
223
    tensor_mask[0, :, :, :] = array_ones
224
    tensor_mask = tf.convert_to_tensor(tensor_mask, dtype=tf.float32)
225
226
    tensor_grid = np.zeros((2, 2, 2, 3))
227
    tensor_grid[:, :, :, 0] = array_ones
228
    tensor_grid = tf.convert_to_tensor(tensor_grid, dtype=tf.float32)
229
230
    get = label.compute_centroid_distance(tensor_mask, tensor_mask, tensor_grid)
231
    expect = np.zeros((3))
232
    assert is_equal_tf(get, expect)
233