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

fuzzylite / fuzzylite / 13598539256

28 Feb 2025 11:56PM UTC coverage: 56.022% (+3.6%) from 52.408%
13598539256

push

github

web-flow
Test: activation methods (#186)

* Getting started

* Add `Activation::toString()` to export to FLL

* Minor fix: Better configure Highest and Lowest

* Proportional: Set activation degree to zero if it is nan

* Rule: use methods to set/get properties inside object.

* RuleBlock: enhanced constructor and fluid interface

* Mock: mocker classes

* Tests: Activation methods

* Fix formatting

* Minor changes

* Sorted members of RuleBlock

42 of 54 new or added lines in 6 files covered. (77.78%)

1 existing line in 1 file now uncovered.

4907 of 8759 relevant lines covered (56.02%)

57516.74 hits per line

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

62.86
/src/rule/Rule.cpp
1
/*
2
fuzzylite (R), a fuzzy logic control library in C++.
3

4
Copyright (C) 2010-2024 FuzzyLite Limited. All rights reserved.
5
Author: Juan Rada-Vilela, PhD <jcrada@fuzzylite.com>.
6

7
This file is part of fuzzylite.
8

9
fuzzylite is free software: you can redistribute it and/or modify it under
10
the terms of the FuzzyLite License included with the software.
11

12
You should have received a copy of the FuzzyLite License along with
13
fuzzylite. If not, see <https://github.com/fuzzylite/fuzzylite/>.
14

15
fuzzylite is a registered trademark of FuzzyLite Limited.
16
*/
17

18
#include "fuzzylite/rule/Rule.h"
19

20
#include "fuzzylite/Exception.h"
21
#include "fuzzylite/Operation.h"
22
#include "fuzzylite/imex/FllExporter.h"
23
#include "fuzzylite/norm/Norm.h"
24

25
namespace fuzzylite {
26
    Rule::Rule(const std::string& text, scalar weight) :
519✔
27
        _enabled(true),
519✔
28
        _text(text),
519✔
29
        _weight(weight),
519✔
30
        _activationDegree(0.0),
519✔
31
        _triggered(false),
519✔
32
        _antecedent(new Antecedent),
519✔
33
        _consequent(new Consequent) {}
1,038✔
34

35
    Rule::Rule(const Rule& other) :
×
36
        _enabled(other._enabled),
×
37
        _text(other._text),
×
38
        _weight(other._weight),
×
39
        _activationDegree(other._activationDegree),
×
40
        _triggered(false),
×
41
        _antecedent(new Antecedent),
×
42
        _consequent(new Consequent) {}
×
43

44
    Rule& Rule::operator=(const Rule& other) {
×
45
        if (this != &other) {
×
46
            _enabled = other._enabled;
×
47
            _text = other._text;
×
48
            _weight = other._weight;
×
49
            _activationDegree = other._activationDegree;
×
50
            _triggered = other._triggered;
×
51
            _antecedent.reset(new Antecedent);
×
52
            _consequent.reset(new Consequent);
×
53
        }
54
        return *this;
×
55
    }
56

57
    Rule::~Rule() {
1,644✔
58
        if (_antecedent.get())
1,038✔
59
            _antecedent->unload();
1,038✔
60
        if (_consequent.get())
1,038✔
61
            _consequent->unload();
1,038✔
62
    }
1,644✔
63

64
    void Rule::setText(const std::string& text) {
819✔
65
        this->_text = text;
819✔
66
    }
819✔
67

68
    std::string Rule::getText() const {
786✔
69
        return this->_text;
786✔
70
    }
71

72
    void Rule::setWeight(scalar weight) {
303✔
73
        this->_weight = weight;
303✔
74
    }
303✔
75

76
    scalar Rule::getWeight() const {
284,759✔
77
        return this->_weight;
284,759✔
78
    }
79

80
    void Rule::setAntecedent(Antecedent* antecedent) {
324✔
81
        this->_antecedent.reset(antecedent);
324✔
82
    }
324✔
83

84
    Antecedent* Rule::getAntecedent() const {
1,993,940✔
85
        return this->_antecedent.get();
1,993,940✔
86
    }
87

88
    void Rule::setConsequent(Consequent* consequent) {
324✔
89
        this->_consequent.reset(consequent);
324✔
90
    }
324✔
91

92
    Consequent* Rule::getConsequent() const {
1,909,164✔
93
        return this->_consequent.get();
1,909,164✔
94
    }
95

96
    void Rule::setEnabled(bool enabled) {
303✔
97
        this->_enabled = enabled;
303✔
98
    }
303✔
99

100
    bool Rule::isEnabled() const {
284,837✔
101
        return this->_enabled;
284,837✔
102
    }
103

104
    void Rule::setActivationDegree(scalar activationDegree) {
569,992✔
105
        this->_activationDegree = activationDegree;
569,992✔
106
    }
569,992✔
107

108
    scalar Rule::getActivationDegree() const {
769,452✔
109
        return this->_activationDegree;
769,452✔
110
    }
111

112
    void Rule::deactivate() {
285,224✔
113
        setActivationDegree(0.0);
285,224✔
114
        setTriggered(false);
285,224✔
115
    }
285,224✔
116

117
    scalar Rule::activateWith(const TNorm* conjunction, const SNorm* disjunction) {
284,759✔
118
        if (not isLoaded())
284,759✔
119
            throw Exception("[rule error] the following rule is not loaded: " + getText(), FL_AT);
×
120
        setActivationDegree(getWeight() * getAntecedent()->activationDegree(conjunction, disjunction));
284,759✔
121
        return getActivationDegree();
284,759✔
122
    }
123

124
    void Rule::trigger(const TNorm* implication) {
284,837✔
125
        if (not isLoaded())
284,837✔
126
            throw Exception("[rule error] the following rule is not loaded: " + getText(), FL_AT);
×
127
        if (isEnabled() and Op::isGt(getActivationDegree(), 0.0)) {
284,837✔
128
            FL_DBG("[firing with " << Op::str(getActivationDegree()) << "] " << toString());
199,983✔
129
            getConsequent()->modify(getActivationDegree(), implication);
199,983✔
130
            setTriggered(true);
199,983✔
131
        }
132
    }
284,837✔
133

134
    bool Rule::isTriggered() const {
162✔
135
        return this->_triggered;
162✔
136
    }
137

138
    void Rule::setTriggered(bool triggered) {
485,207✔
139
        this->_triggered = triggered;
485,207✔
140
    }
485,207✔
141

142
    bool Rule::isLoaded() const {
854,277✔
143
        return getAntecedent() and getAntecedent()->isLoaded() and getConsequent() and getConsequent()->isLoaded();
854,277✔
144
    }
145

146
    void Rule::unload() {
×
147
        deactivate();
×
148
        if (getAntecedent())
×
149
            getAntecedent()->unload();
×
150
        if (getConsequent())
×
151
            getConsequent()->unload();
×
152
    }
×
153

154
    void Rule::load(const Engine* engine) {
300✔
155
        load(getText(), engine);
300✔
156
    }
300✔
157

158
    void Rule::load(const std::string& rule, const Engine* engine) {
303✔
159
        deactivate();
303✔
160
        setEnabled(true);
303✔
161
        setText(rule);
303✔
162
        std::istringstream tokenizer(rule.substr(0, rule.find_first_of('#')));
303✔
163
        std::string token;
303✔
164
        std::ostringstream ossAntecedent, ossConsequent;
303✔
165
        scalar weight = 1.0;
303✔
166

167
        enum FSM { S_NONE, S_IF, S_THEN, S_WITH, S_END };
168

169
        FSM state = S_NONE;
303✔
170
        try {
171
            while (tokenizer >> token) {
4,077✔
172
                switch (state) {
3,774✔
173
                    case S_NONE:
303✔
174
                        if (token == Rule::ifKeyword())
303✔
175
                            state = S_IF;
303✔
176
                        else {
177
                            std::ostringstream ex;
×
178
                            ex << "[syntax error] expected keyword <" << Rule::ifKeyword() << ">, but found <" << token
×
179
                               << "> in rule: " << rule;
×
180
                            throw Exception(ex.str(), FL_AT);
×
181
                        }
×
182
                        break;
303✔
183
                    case S_IF:
2,364✔
184
                        if (token == Rule::thenKeyword())
2,364✔
185
                            state = S_THEN;
303✔
186
                        else
187
                            ossAntecedent << token << " ";
2,061✔
188
                        break;
2,364✔
189
                    case S_THEN:
1,105✔
190
                        if (token == Rule::withKeyword())
1,105✔
191
                            state = S_WITH;
2✔
192
                        else
193
                            ossConsequent << token << " ";
1,103✔
194
                        break;
1,105✔
195
                    case S_WITH:
2✔
196
                        try {
197
                            weight = Op::toScalar(token);
2✔
198
                            state = S_END;
2✔
199
                        } catch (Exception& e) {
×
200
                            std::ostringstream ex;
×
201
                            ex << "[syntax error] expected a numeric value as the weight of the rule: " << rule;
×
202
                            e.append(ex.str(), FL_AT);
×
203
                            throw;
×
204
                        }
×
205
                        break;
2✔
206
                    case S_END: {
×
207
                        std::ostringstream ex;
×
208
                        ex << "[syntax error] unexpected token <" << token << "> at the end of rule";
×
209
                        throw Exception(ex.str(), FL_AT);
×
210
                    }
×
211

212
                    default:
×
213
                        std::ostringstream ex;
×
214
                        ex << "[syntax error] unexpected state <" << state << ">";
×
215
                        throw Exception(ex.str(), FL_AT);
×
216
                }
217
            }
218
            if (state == S_NONE) {
303✔
219
                std::ostringstream ex;
×
220
                ex << "[syntax error] " << (rule.empty() ? "empty rule" : ("ignored rule: " + rule));
×
221
                throw Exception(ex.str(), FL_AT);
×
222
            } else if (state == S_IF) {
303✔
223
                std::ostringstream ex;
×
224
                ex << "[syntax error] keyword <" << Rule::thenKeyword() << "> not found in rule: " << rule;
×
225
                throw Exception(ex.str(), FL_AT);
×
226
            } else if (state == S_WITH) {
303✔
227
                std::ostringstream ex;
×
228
                ex << "[syntax error] expected a numeric value as the weight of the rule: " << rule;
×
229
                throw Exception(ex.str(), FL_AT);
×
230
            }
×
231

232
            getAntecedent()->load(ossAntecedent.str(), engine);
303✔
233
            getConsequent()->load(ossConsequent.str(), engine);
303✔
234
            setWeight(weight);
303✔
UNCOV
235
        } catch (...) {
×
236
            unload();
×
237
            throw;
×
238
        }
×
239
    }
303✔
240

241
    std::string Rule::toString() const {
×
242
        return FllExporter().toString(this);
×
243
    }
244

245
    Rule* Rule::clone() const {
×
246
        return new Rule(*this);
×
247
    }
248

249
    Rule* Rule::parse(const std::string& rule, const Engine* engine) {
3✔
250
        FL_unique_ptr<Rule> result(new Rule);
6✔
251
        result->load(rule, engine);
3✔
252
        return result.release();
6✔
253
    }
3✔
254
}
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