• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

fuzzylite / pyfuzzylite / 18148505153

14 Sep 2025 09:31PM UTC coverage: 95.743% (+0.2%) from 95.511%
18148505153

push

github

web-flow
8.0.6 (#96)

2 of 2 new or added lines in 2 files covered. (100.0%)

1 existing line in 1 file now uncovered.

6252 of 6530 relevant lines covered (95.74%)

0.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.33
/tests/test_vectorization.py
1
"""pyfuzzylite: a fuzzy logic control library in Python.
2

3
This file is part of pyfuzzylite.
4

5
Repository: https://github.com/fuzzylite/pyfuzzylite/
6

7
License: FuzzyLite License
8

9
Copyright: FuzzyLite by Juan Rada-Vilela. All rights reserved.
10
"""
11

12
from __future__ import annotations
1✔
13

14
import copy
1✔
15
import unittest
1✔
16

17
import numpy as np
1✔
18

19
import fuzzylite as fl
1✔
20
from fuzzylite.examples import mamdani, takagi_sugeno, tsukamoto
1✔
21

22

23
class AssertIntegration:
1✔
24
    """Asserts integration of a Mamdani or Takagi-Sugeno system with a defuzzifier."""
25

26
    def __init__(self, engine: fl.Engine) -> None:
1✔
27
        """Constructor.
28

29
        @param engine is the engine to test on.
30
        @param vectorize is whether to test vectorization.
31
        """
32
        self.engine = copy.deepcopy(engine)
1✔
33

34
    def assert_that(
1✔
35
        self,
36
        defuzzifier: fl.Defuzzifier,
37
        input_expected: dict[float, float] | list[tuple[float, float]],
38
    ) -> None:
39
        """Asserts integration of a Mamdani or Takagi-Sugeno system with a defuzzifier."""
40
        for output in self.engine.output_variables:
1✔
41
            output.defuzzifier = defuzzifier
1✔
42
        if isinstance(input_expected, dict):
1✔
43
            input_expected = list(input_expected.items())
1✔
44
        inputs = fl.array([x[0] for x in input_expected])
1✔
45
        expected_outputs = fl.array([x[1] for x in input_expected])
1✔
46

47
        self.engine.input_variables[0].value = inputs
1✔
48
        self.engine.process()
1✔
49
        obtained = self.engine.output_variables[0].value
1✔
50
        np.testing.assert_allclose(
1✔
51
            obtained,
52
            expected_outputs,
53
            err_msg=f"{fl.Op.class_name(defuzzifier)}([{inputs}]) = {obtained}, but expected {expected_outputs}",
54
            atol=fl.settings.atol,
55
            rtol=fl.settings.rtol,
56
        )
57

58

59
class TestMamdani(unittest.TestCase):
1✔
60
    """Tests for Integral Defuzzifiers."""
61

62
    def test_simple_mamdani_bisector_integration(self) -> None:
1✔
63
        """Test a simple integration with Bisector."""
64
        AssertIntegration(mamdani.simple_dimmer.SimpleDimmer().engine).assert_that(
1✔
65
            fl.Bisector(),
66
            {
67
                0.0: fl.nan,
68
                1.0: fl.nan,
69
                0.25: 0.75,
70
                0.375: 0.625,
71
                0.5: 0.5,
72
                0.625: 0.375,
73
                0.675: 0.304,
74
                0.75: 0.25,
75
            },
76
        )
77

78
    def test_simple_mamdani_bisector_integration_with_locking(self) -> None:
1✔
79
        """Test a simple integration with Bisector."""
80
        engine = mamdani.simple_dimmer.SimpleDimmer().engine
1✔
81
        for output_variable in engine.output_variables:
1✔
82
            output_variable.lock_previous = True
1✔
83
            output_variable.default_value = 0.2468
1✔
84
            output_variable.value = np.array([1.234, 9.876])
1✔
85

86
        AssertIntegration(engine).assert_that(
1✔
87
            fl.Bisector(),
88
            [
89
                # replace first nans with last value
90
                (0.0, 9.876),  # (0.0, fl.nan),
91
                (1.0, 9.876),  # (1.0, fl.nan),
92
                (0.25, 0.75),
93
                (0.375, 0.625),
94
                (0.5, 0.5),
95
                # replace nans with previous values
96
                (0.0, 0.5),  # (0.0, fl.nan),
97
                (1.0, 0.5),  # (1.0, fl.nan),
98
            ],
99
        )
100

101
        engine.restart()
1✔
102

103
        AssertIntegration(engine).assert_that(
1✔
104
            fl.Bisector(),
105
            [
106
                # replace first nans default value
107
                (0.0, 0.2468),  # 0.0: fl.nan,
108
                (1.0, 0.2468),  # 1.0: fl.nan,
109
                (0.25, 0.75),
110
                (0.375, 0.625),
111
                (0.5, 0.5),
112
                # replace nans with previous values
113
                (0.0, 0.5),  # (0.0, fl.nan),
114
                (1.0, 0.5),  # (1.0, fl.nan),
115
            ],
116
        )
117

118
    def test_simple_mamdani_centroid_integration(self) -> None:
1✔
119
        """Test a simple integration with Centroid."""
120
        AssertIntegration(mamdani.simple_dimmer.SimpleDimmer().engine).assert_that(
1✔
121
            fl.Centroid(),
122
            {
123
                0.0: fl.nan,
124
                1.0: fl.nan,
125
                0.25: 0.75,
126
                0.375: 0.625,
127
                0.5: 0.5,
128
                0.625: 0.375,
129
                0.675: 0.334,
130
                0.75: 0.25,
131
            },
132
        )
133

134
    def test_simple_mamdani_lom_integration(self) -> None:
1✔
135
        """Test a simple integration with LargestOfMaximum."""
136
        AssertIntegration(mamdani.simple_dimmer.SimpleDimmer().engine).assert_that(
1✔
137
            fl.LargestOfMaximum(),
138
            {
139
                0.0: fl.nan,
140
                1.0: fl.nan,
141
                0.25: 0.75,
142
                0.375: 0.875,
143
                0.5: 0.5,
144
                0.625: 0.625,
145
                0.675: 0.325,
146
                0.75: 0.25,
147
            },
148
        )
149

150
    def test_simple_mamdani_mom_integration(self) -> None:
1✔
151
        """Test a simple integration with MeanOfMaximum."""
152
        AssertIntegration(mamdani.simple_dimmer.SimpleDimmer().engine).assert_that(
1✔
153
            fl.MeanOfMaximum(),
154
            {
155
                0.0: fl.nan,
156
                1.0: fl.nan,
157
                0.25: 0.75,
158
                0.375: 0.625,
159
                0.5: 0.5,
160
                0.625: 0.375,
161
                0.675: 0.25,
162
                0.75: 0.25,
163
            },
164
        )
165

166
    def test_simple_mamdani_som_integration(self) -> None:
1✔
167
        """Test a simple integration without vectorization."""
168
        AssertIntegration(mamdani.simple_dimmer.SimpleDimmer().engine).assert_that(
1✔
169
            fl.SmallestOfMaximum(),
170
            {
171
                0.0: fl.nan,
172
                1.0: fl.nan,
173
                0.25: 0.75,
174
                0.375: 0.375,
175
                0.5: 0.5,
176
                0.625: 0.125,
177
                0.675: 0.175,
178
                0.75: 0.25,
179
            },
180
        )
181

182

183
class TestWeightedDefuzzifier(unittest.TestCase):
1✔
184
    """Tests for Weighted defuzzifiers."""
185

186
    def test_simple_takagisugeno_avg_integration(self) -> None:
1✔
187
        """Test a simple integration with WeightedAverage."""
188
        AssertIntegration(takagi_sugeno.simple_dimmer.SimpleDimmer().engine).assert_that(
1✔
189
            fl.WeightedAverage(),
190
            {
191
                0.0: fl.nan,
192
                1.0: fl.nan,
193
                0.25: 0.75,
194
                0.375: 0.625,
195
                0.5: 0.5,
196
                0.625: 0.375,
197
                0.675: 0.325,
198
                0.75: 0.25,
199
            },
200
        )
201

202
    def test_simple_takagisugeno_avg_integration_locking_previous(self) -> None:
1✔
203
        """Test a simple integration with WeightedAverage."""
204
        engine = takagi_sugeno.simple_dimmer.SimpleDimmer().engine
1✔
205
        for output_variable in engine.output_variables:
1✔
206
            output_variable.lock_previous = True
1✔
207
            output_variable.default_value = 0.2468
1✔
208
            output_variable.value = np.array([1.234, 9.876])
1✔
209

210
        AssertIntegration(engine).assert_that(
1✔
211
            fl.WeightedAverage(),
212
            [
213
                # replace first nans with last value
214
                (0.0, 9.876),  # 0.0: fl.nan,
215
                (1.0, 9.876),  # 1.0: fl.nan,
216
                (0.25, 0.75),
217
                (0.375, 0.625),
218
                (0.5, 0.5),
219
                # replace nans with previous values
220
                (0.0, 0.5),
221
                (1.0, 0.5),
222
            ],
223
        )
224

225
        engine.restart()
1✔
226

227
        AssertIntegration(engine).assert_that(
1✔
228
            fl.WeightedAverage(),
229
            [
230
                # replace first nans with default value
231
                (0.0, 0.2468),  # 0.0: fl.nan,
232
                (1.0, 0.2468),  # 1.0: fl.nan,
233
                (0.25, 0.75),
234
                (0.375, 0.625),
235
                (0.5, 0.5),
236
                # replace nans with previous values
237
                (0.0, 0.5),
238
                (1.0, 0.5),
239
            ],
240
        )
241

242
    def test_simple_takagisugeno_sum_integration(self) -> None:
1✔
243
        """Test a simple integration with WeightedSum."""
244
        AssertIntegration(takagi_sugeno.simple_dimmer.SimpleDimmer().engine).assert_that(
1✔
245
            fl.WeightedSum(),
246
            {
247
                0.0: np.nan,
248
                1.0: np.nan,
249
                0.25: 0.75,
250
                0.375: 0.625,
251
                0.5: 0.5,
252
                0.625: 0.375,
253
                0.675: 0.325,
254
                0.75: 0.25,
255
            },
256
        )
257

258
    def test_simple_tsukamoto_avg_integration(self) -> None:
1✔
259
        """Test a simple integration with WeightedAverage."""
260
        AssertIntegration(tsukamoto.tsukamoto.Tsukamoto().engine).assert_that(
1✔
261
            fl.WeightedAverage(),
262
            {
263
                -20: 0.014,
264
                -10.0: 0.255,
265
                -7.5: 0.272,
266
                -5: 0.313,
267
                -2.5: 0.375,
268
                -1: 0.392,
269
                0: 0.399,
270
                1: 0.405,
271
                2.5: 0.426,
272
                5: 0.674,
273
                7.5: 0.964,
274
                10: 0.994,
275
                20: 0.702,
276
                -np.inf: np.nan,
277
                np.inf: np.nan,
278
                np.nan: np.nan,
279
            },
280
        )
281

282
    def test_simple_tsukamoto_sum_integration(self) -> None:
1✔
283
        """Test a simple integration with WeightedSum."""
284
        AssertIntegration(tsukamoto.tsukamoto.Tsukamoto().engine).assert_that(
1✔
285
            fl.WeightedSum(),
286
            {
287
                -20: 0.0,
288
                -10.0: 0.259,
289
                -7.5: 0.290,
290
                -5: 0.313,
291
                -2.5: 0.401,
292
                -1: 0.406,
293
                0: 0.411,
294
                1: 0.420,
295
                2.5: 0.455,
296
                5: 0.675,
297
                7.5: 1.027,
298
                10: 1.009,
299
                20: 0.011,
300
                -np.inf: np.nan,
301
                np.inf: np.nan,
302
                np.nan: np.nan,
303
            },
304
        )
305

306

307
if __name__ == "__main__":
1✔
UNCOV
308
    unittest.main()
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc