1 /*
2  fuzzylite (R), a fuzzy logic control library in C++.
3  Copyright (C) 2010-2017 FuzzyLite Limited. All rights reserved.
4  Author: Juan Rada-Vilela, Ph.D. <jcrada@fuzzylite.com>
5 
6  This file is part of fuzzylite.
7 
8  fuzzylite is free software: you can redistribute it and/or modify it under
9  the terms of the FuzzyLite License included with the software.
10 
11  You should have received a copy of the FuzzyLite License along with
12  fuzzylite. If not, see <http://www.fuzzylite.com/license/>.
13 
14  fuzzylite is a registered trademark of FuzzyLite Limited.
15  */
16 
17 #include "fl/Engine.h"
18 
19 #include "fl/activation/General.h"
20 #include "fl/defuzzifier/WeightedAverage.h"
21 #include "fl/defuzzifier/WeightedSum.h"
22 #include "fl/factory/DefuzzifierFactory.h"
23 #include "fl/factory/FactoryManager.h"
24 #include "fl/imex/FllExporter.h"
25 #include "fl/norm/t/AlgebraicProduct.h"
26 #include "fl/rule/Consequent.h"
27 #include "fl/rule/Expression.h"
28 #include "fl/rule/Rule.h"
29 #include "fl/rule/RuleBlock.h"
30 #include "fl/term/Aggregated.h"
31 #include "fl/term/Constant.h"
32 #include "fl/term/Linear.h"
33 #include "fl/term/Ramp.h"
34 #include "fl/term/Sigmoid.h"
35 #include "fl/term/SShape.h"
36 #include "fl/term/ZShape.h"
37 #include "fl/variable/InputVariable.h"
38 #include "fl/variable/OutputVariable.h"
39 
40 namespace fl {
41 
Engine(const std::string & name)42     Engine::Engine(const std::string& name) : _name(name) { }
43 
Engine(const Engine & other)44     Engine::Engine(const Engine& other) : _name(""), _description("") {
45         copyFrom(other);
46     }
47 
operator =(const Engine & other)48     Engine& Engine::operator=(const Engine& other) {
49         if (this != &other) {
50             for (std::size_t i = 0; i < _ruleBlocks.size(); ++i)
51                 delete _ruleBlocks.at(i);
52             _ruleBlocks.clear();
53             for (std::size_t i = 0; i < _outputVariables.size(); ++i)
54                 delete _outputVariables.at(i);
55             _outputVariables.clear();
56             for (std::size_t i = 0; i < _inputVariables.size(); ++i)
57                 delete _inputVariables.at(i);
58             _inputVariables.clear();
59 
60             copyFrom(other);
61         }
62         return *this;
63     }
64 
copyFrom(const Engine & other)65     void Engine::copyFrom(const Engine& other) {
66         _name = other._name;
67         _description = other._description;
68         for (std::size_t i = 0; i < other._inputVariables.size(); ++i)
69             _inputVariables.push_back(new InputVariable(*other._inputVariables.at(i)));
70         for (std::size_t i = 0; i < other._outputVariables.size(); ++i)
71             _outputVariables.push_back(new OutputVariable(*other._outputVariables.at(i)));
72 
73         updateReferences();
74 
75         for (std::size_t i = 0; i < other._ruleBlocks.size(); ++i) {
76             RuleBlock* ruleBlock = new RuleBlock(*other._ruleBlocks.at(i));
77             try {
78                 ruleBlock->loadRules(this);
79             } catch (...) {
80                 //ignore
81             }
82             _ruleBlocks.push_back(ruleBlock);
83         }
84     }
85 
updateReferences() const86     void Engine::updateReferences() const {
87         std::vector<Variable*> myVariables = variables();
88         for (std::size_t i = 0; i < myVariables.size(); ++i) {
89             Variable* variable = myVariables.at(i);
90             for (std::size_t t = 0; t < variable->numberOfTerms(); ++t) {
91                 variable->getTerm(t)->updateReference(this);
92             }
93         }
94     }
95 
~Engine()96     Engine::~Engine() {
97         for (std::size_t i = 0; i < _ruleBlocks.size(); ++i)
98             delete _ruleBlocks.at(i);
99         for (std::size_t i = 0; i < _outputVariables.size(); ++i)
100             delete _outputVariables.at(i);
101         for (std::size_t i = 0; i < _inputVariables.size(); ++i)
102             delete _inputVariables.at(i);
103     }
104 
configure(const std::string & conjunction,const std::string & disjunction,const std::string & implication,const std::string & aggregation,const std::string & defuzzifier,const std::string & activation)105     void Engine::configure(const std::string& conjunction, const std::string& disjunction,
106             const std::string& implication, const std::string& aggregation,
107             const std::string& defuzzifier, const std::string& activation) {
108         TNormFactory* tnormFactory = FactoryManager::instance()->tnorm();
109         SNormFactory* snormFactory = FactoryManager::instance()->snorm();
110         DefuzzifierFactory* defuzzFactory = FactoryManager::instance()->defuzzifier();
111         ActivationFactory* activationFactory = FactoryManager::instance()->activation();
112 
113         TNorm* conjunctionObject = tnormFactory->constructObject(conjunction);
114         SNorm* disjunctionObject = snormFactory->constructObject(disjunction);
115         TNorm* implicationObject = tnormFactory->constructObject(implication);
116         SNorm* aggregationObject = snormFactory->constructObject(aggregation);
117         Defuzzifier* defuzzifierObject = defuzzFactory->constructObject(defuzzifier);
118         Activation* activationObject = activationFactory->constructObject(activation);
119 
120         configure(conjunctionObject, disjunctionObject,
121                 implicationObject, aggregationObject, defuzzifierObject,
122                 activationObject);
123     }
124 
configure(TNorm * conjunction,SNorm * disjunction,TNorm * implication,SNorm * aggregation,Defuzzifier * defuzzifier,Activation * activation)125     void Engine::configure(TNorm* conjunction, SNorm* disjunction,
126             TNorm* implication, SNorm* aggregation, Defuzzifier* defuzzifier,
127             Activation* activation) {
128         for (std::size_t i = 0; i < numberOfRuleBlocks(); ++i) {
129             RuleBlock* ruleBlock = ruleBlocks().at(i);
130             ruleBlock->setConjunction(conjunction ? conjunction->clone() : fl::null);
131             ruleBlock->setDisjunction(disjunction ? disjunction->clone() : fl::null);
132             ruleBlock->setImplication(implication ? implication->clone() : fl::null);
133             ruleBlock->setActivation(activation ? activation->clone() : new General);
134         }
135 
136         for (std::size_t i = 0; i < numberOfOutputVariables(); ++i) {
137             OutputVariable* outputVariable = getOutputVariable(i);
138             outputVariable->setDefuzzifier(defuzzifier ? defuzzifier->clone() : fl::null);
139             outputVariable->setAggregation(aggregation ? aggregation->clone() : fl::null);
140         }
141         if (defuzzifier) delete defuzzifier;
142         if (aggregation) delete aggregation;
143         if (implication) delete implication;
144         if (disjunction) delete disjunction;
145         if (conjunction) delete conjunction;
146         if (activation) delete activation;
147     }
148 
isReady(std::string * status) const149     bool Engine::isReady(std::string* status) const {
150         std::ostringstream ss;
151         if (inputVariables().empty()) {
152             ss << "- Engine <" << getName() << "> has no input variables\n";
153         }
154         for (std::size_t i = 0; i < inputVariables().size(); ++i) {
155             InputVariable* inputVariable = inputVariables().at(i);
156             if (not inputVariable) {
157                 ss << "- Engine <" << getName() << "> has a fl::null input variable at index <" << i << ">\n";
158             }
159             /*else if (inputVariable->terms().empty()) {
160             ignore because sometimes inputs can be empty: takagi-sugeno/matlab/slcpp1.fis
161                             ss << "- Input variable <" << _inputVariables.at(i)->getName() << ">"
162                                     << " has no terms\n";
163             }*/
164         }
165 
166         if (outputVariables().empty()) {
167             ss << "- Engine <" << _name << "> has no output variables\n";
168         }
169         for (std::size_t i = 0; i < outputVariables().size(); ++i) {
170             OutputVariable* outputVariable = outputVariables().at(i);
171             if (not outputVariable) {
172                 ss << "- Engine <" << getName() << "> has a fl::null output variable at index <" << i << ">\n";
173             } else {
174                 if (outputVariable->terms().empty()) {
175                     ss << "- Output variable <" << outputVariable->getName() << ">"
176                             << " has no terms\n";
177                 }
178                 Defuzzifier* defuzzifier = outputVariable->getDefuzzifier();
179                 if (not defuzzifier) {
180                     ss << "- Output variable <" << outputVariable->getName() << ">"
181                             << " has no defuzzifier\n";
182                 }
183                 SNorm* aggregation = outputVariable->fuzzyOutput()->getAggregation();
184                 if (not aggregation and dynamic_cast<IntegralDefuzzifier*> (defuzzifier)) {
185                     ss << "- Output variable <" << outputVariable->getName() << ">"
186                             << " has no aggregation operator\n";
187                 }
188             }
189         }
190 
191         if (ruleBlocks().empty()) {
192             ss << "- Engine <" << getName() << "> has no rule blocks\n";
193         }
194         for (std::size_t i = 0; i < ruleBlocks().size(); ++i) {
195             RuleBlock* ruleblock = ruleBlocks().at(i);
196             if (not ruleblock) {
197                 ss << "- Engine <" << getName() << "> has a fl::null rule block at index <" << i << ">\n";
198             } else {
199                 if (ruleblock->rules().empty()) {
200                     ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has no rules\n";
201                 }
202                 int requiresConjunction = 0;
203                 int requiresDisjunction = 0;
204                 int requiresImplication = 0;
205                 for (std::size_t r = 0; r < ruleblock->numberOfRules(); ++r) {
206                     Rule* rule = ruleblock->getRule(r);
207                     if (not rule) {
208                         ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName()
209                                 << "> has a fl::null rule at index <" << r << ">\n";
210                     } else {
211                         std::size_t thenIndex = rule->getText().find(" " + Rule::thenKeyword() + " ");
212                         std::size_t andIndex = rule->getText().find(" " + Rule::andKeyword() + " ");
213                         std::size_t orIndex = rule->getText().find(" " + Rule::orKeyword() + " ");
214                         if (andIndex != std::string::npos and andIndex < thenIndex) {
215                             ++requiresConjunction;
216                         }
217                         if (orIndex != std::string::npos and orIndex < thenIndex) {
218                             ++requiresDisjunction;
219                         }
220                         if (rule->isLoaded()) {
221                             Consequent* consequent = rule->getConsequent();
222                             for (std::size_t c = 0; c < consequent->conclusions().size(); ++c) {
223                                 Proposition* proposition = consequent->conclusions().at(c);
224                                 const OutputVariable* outputVariable =
225                                         dynamic_cast<const OutputVariable*> (proposition->variable);
226                                 if (outputVariable and dynamic_cast<IntegralDefuzzifier*> (outputVariable->getDefuzzifier())) {
227                                     ++requiresImplication;
228                                     break;
229                                 }
230                             }
231                         }
232                     }
233                 }
234                 const TNorm* conjunction = ruleblock->getConjunction();
235                 if (requiresConjunction > 0 and not conjunction) {
236                     ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has no conjunction operator\n";
237                     ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has "
238                             << requiresConjunction << " rules that require conjunction operator\n";
239                 }
240                 const SNorm* disjunction = ruleblock->getDisjunction();
241                 if (requiresDisjunction > 0 and not disjunction) {
242                     ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has no disjunction operator\n";
243                     ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has "
244                             << requiresDisjunction << " rules that require disjunction operator\n";
245                 }
246                 const TNorm* implication = ruleblock->getImplication();
247                 if (requiresImplication > 0 and not implication) {
248                     ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has no implication operator\n";
249                     ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has "
250                             << requiresImplication << " rules that require implication operator\n";
251                 }
252             }
253         }
254         if (status) *status = ss.str();
255         return ss.str().empty();
256     }
257 
restart()258     void Engine::restart() {
259         for (std::size_t i = 0; i < inputVariables().size(); ++i) {
260             inputVariables().at(i)->setValue(fl::nan);
261         }
262         for (std::size_t i = 0; i < outputVariables().size(); ++i) {
263             outputVariables().at(i)->clear();
264         }
265     }
266 
complexity() const267     Complexity Engine::complexity() const {
268         Complexity result;
269         for (std::size_t i = 0; i < _ruleBlocks.size(); ++i) {
270             const RuleBlock* ruleBlock = _ruleBlocks.at(i);
271             if (ruleBlock->isEnabled()) {
272                 result += ruleBlock->complexity();
273             }
274         }
275         return result;
276     }
277 
process()278     void Engine::process() {
279         for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
280             _outputVariables.at(i)->fuzzyOutput()->clear();
281         }
282 
283         FL_DEBUG_BEGIN;
284         FL_DBG("===============");
285         FL_DBG("CURRENT INPUTS:");
286         for (std::size_t i = 0; i < _inputVariables.size(); ++i) {
287             InputVariable* inputVariable = _inputVariables.at(i);
288             scalar inputValue = inputVariable->getValue();
289             if (inputVariable->isEnabled()) {
290                 FL_DBG(inputVariable->getName() << ".input = " << Op::str(inputValue));
291                 FL_DBG(inputVariable->getName() << ".fuzzy = " << inputVariable->fuzzify(inputValue));
292             } else {
293                 FL_DBG(inputVariable->getName() << ".enabled = false");
294             }
295         }
296         FL_DEBUG_END;
297 
298 
299         for (std::size_t i = 0; i < _ruleBlocks.size(); ++i) {
300             RuleBlock* ruleBlock = _ruleBlocks.at(i);
301             if (ruleBlock->isEnabled()) {
302                 FL_DBG("===============");
303                 FL_DBG("RULE BLOCK: " << ruleBlock->getName());
304                 ruleBlock->activate();
305             }
306         }
307 
308         for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
309             _outputVariables.at(i)->defuzzify();
310         }
311 
312         FL_DEBUG_BEGIN;
313         FL_DBG("===============");
314         FL_DBG("CURRENT OUTPUTS:");
315         for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
316             OutputVariable* outputVariable = _outputVariables.at(i);
317             if (outputVariable->isEnabled()) {
318                 FL_DBG(outputVariable->getName() << ".default = "
319                         << outputVariable->getDefaultValue());
320 
321                 FL_DBG(outputVariable->getName() << ".lockValueInRange = "
322                         << outputVariable->isLockValueInRange());
323 
324                 FL_DBG(outputVariable->getName() << ".lockPreviousValue= "
325                         << outputVariable->isLockPreviousValue());
326 
327                 scalar output = outputVariable->getValue();
328                 FL_DBG(outputVariable->getName() << ".output = " << output);
329                 FL_DBG(outputVariable->getName() << ".fuzzy = " <<
330                         outputVariable->fuzzify(output));
331                 FL_DBG(outputVariable->fuzzyOutput()->toString());
332             } else {
333                 FL_DBG(outputVariable->getName() << ".enabled = false");
334             }
335         }
336         FL_DBG("==============");
337         FL_DEBUG_END;
338     }
339 
setName(const std::string & name)340     void Engine::setName(const std::string& name) {
341         this->_name = name;
342     }
343 
getName() const344     std::string Engine::getName() const {
345         return this->_name;
346     }
347 
setDescription(const std::string & description)348     void Engine::setDescription(const std::string& description) {
349         this->_description = description;
350     }
351 
getDescription() const352     std::string Engine::getDescription() const {
353         return this->_description;
354     }
355 
toString() const356     std::string Engine::toString() const {
357         return FllExporter().toString(this);
358     }
359 
type(std::string * name,std::string * reason) const360     Engine::Type Engine::type(std::string* name, std::string* reason) const {
361         if (outputVariables().empty()) {
362             if (name) *name = "Unknown";
363             if (reason) *reason = "- Engine has no output variables";
364             return Engine::Unknown;
365         }
366 
367         //Mamdani
368         bool mamdani = true;
369         for (std::size_t i = 0; mamdani and i < outputVariables().size(); ++i) {
370             OutputVariable* outputVariable = outputVariables().at(i);
371             //Defuzzifier must be integral
372             mamdani = mamdani and dynamic_cast<IntegralDefuzzifier*> (outputVariable->getDefuzzifier());
373         }
374         //Larsen
375         bool larsen = mamdani and not ruleBlocks().empty();
376         //Larsen is Mamdani with AlgebraicProduct as Implication
377         if (mamdani) {
378             for (std::size_t i = 0; larsen and i < ruleBlocks().size(); ++i) {
379                 RuleBlock* ruleBlock = ruleBlocks().at(i);
380                 larsen = larsen and dynamic_cast<const AlgebraicProduct*> (ruleBlock->getImplication());
381             }
382         }
383         if (larsen) {
384             if (name) *name = "Larsen";
385             if (reason) *reason = "- Output variables have integral defuzzifiers\n"
386                     "- Implication in rule blocks is the algebraic product T-Norm";
387             return Engine::Larsen;
388         }
389         if (mamdani) {
390             if (name) *name = "Mamdani";
391             if (reason) *reason = "-Output variables have integral defuzzifiers";
392             return Engine::Mamdani;
393         }
394         //Else, keep checking
395 
396         //TakagiSugeno
397         bool takagiSugeno = true;
398         for (std::size_t i = 0; takagiSugeno and i < outputVariables().size(); ++i) {
399             OutputVariable* outputVariable = outputVariables().at(i);
400             //Defuzzifier is Weighted
401             WeightedDefuzzifier* weightedDefuzzifier =
402                     dynamic_cast<WeightedDefuzzifier*> (outputVariable->getDefuzzifier());
403 
404             takagiSugeno = takagiSugeno and weightedDefuzzifier and
405                     (weightedDefuzzifier->getType() == WeightedDefuzzifier::Automatic or
406                     weightedDefuzzifier->getType() == WeightedDefuzzifier::TakagiSugeno);
407 
408             if (takagiSugeno) {
409                 //Takagi-Sugeno has only Constant, Linear or Function terms
410                 for (std::size_t t = 0; takagiSugeno and t < outputVariable->numberOfTerms(); ++t) {
411                     Term* term = outputVariable->getTerm(t);
412                     takagiSugeno = takagiSugeno and
413                             weightedDefuzzifier->inferType(term) == WeightedDefuzzifier::TakagiSugeno;
414                 }
415             }
416         }
417         if (takagiSugeno) {
418             if (name) *name = "Takagi-Sugeno";
419             if (reason) *reason = "- Output variables have weighted defuzzifiers\n"
420                     "- Output variables have constant, linear or function terms";
421             return Engine::TakagiSugeno;
422         }
423 
424         //Tsukamoto
425         bool tsukamoto = true;
426         for (std::size_t i = 0; tsukamoto and i < outputVariables().size(); ++i) {
427             OutputVariable* outputVariable = outputVariables().at(i);
428             //Defuzzifier is Weighted
429             WeightedDefuzzifier* weightedDefuzzifier =
430                     dynamic_cast<WeightedDefuzzifier*> (outputVariable->getDefuzzifier());
431 
432             tsukamoto = tsukamoto and weightedDefuzzifier and
433                     (weightedDefuzzifier->getType() == WeightedDefuzzifier::Automatic or
434                     weightedDefuzzifier->getType() == WeightedDefuzzifier::Tsukamoto);
435             if (tsukamoto) {
436                 //Tsukamoto has only monotonic terms: Concave, Ramp, Sigmoid, SShape, or ZShape
437                 for (std::size_t t = 0; tsukamoto and t < outputVariable->numberOfTerms(); ++t) {
438                     Term* term = outputVariable->getTerm(t);
439                     tsukamoto = tsukamoto and term->isMonotonic();
440                 }
441             }
442         }
443         if (tsukamoto) {
444             if (name) *name = "Tsukamoto";
445             if (reason) *reason = "- Output variables have weighted defuzzifiers\n"
446                     "- Output variables only have monotonic terms";
447             return Engine::Tsukamoto;
448         }
449 
450         //Inverse Tsukamoto
451         bool inverseTsukamoto = true;
452         for (std::size_t i = 0; inverseTsukamoto and i < outputVariables().size(); ++i) {
453             OutputVariable* outputVariable = outputVariables().at(i);
454             //Defuzzifier cannot be integral
455             WeightedDefuzzifier* weightedDefuzzifier =
456                     dynamic_cast<WeightedDefuzzifier*> (outputVariable->getDefuzzifier());
457             inverseTsukamoto = inverseTsukamoto and weightedDefuzzifier;
458         }
459         if (inverseTsukamoto) {
460             if (name) *name = "Inverse Tsukamoto";
461             if (reason) *reason = "- Output variables have weighted defuzzifiers\n"
462                     "- Output variables do not only have constant, linear or function terms\n"
463                     "- Output variables do not only have monotonic terms";
464             return Engine::InverseTsukamoto;
465         }
466 
467         bool hybrid = true;
468         for (std::size_t i = 0; i < outputVariables().size(); ++i) {
469             OutputVariable* outputVariable = outputVariables().at(i);
470             //Output variables have non-fl::null defuzzifiers
471             hybrid = hybrid and outputVariable->getDefuzzifier();
472         }
473         if (hybrid) {
474             if (name) *name = "Hybrid";
475             if (reason) *reason = "- Output variables have different types of defuzzifiers";
476             return Engine::Hybrid;
477         }
478 
479         if (name) *name = "Unknown";
480         if (reason) *reason = "- One or more output variables do not have a defuzzifier";
481         return Engine::Unknown;
482     }
483 
clone() const484     Engine* Engine::clone() const {
485         return new Engine(*this);
486     }
487 
variables() const488     std::vector<Variable*> Engine::variables() const {
489         std::vector<Variable*> result;
490         result.reserve(inputVariables().size() + outputVariables().size());
491         result.insert(result.end(), inputVariables().begin(), inputVariables().end());
492         result.insert(result.end(), outputVariables().begin(), outputVariables().end());
493         return result;
494     }
495 
496     /**
497      * Operations for InputVariables
498      */
setInputValue(const std::string & name,scalar value)499     void Engine::setInputValue(const std::string& name, scalar value) {
500         InputVariable* inputVariable = getInputVariable(name);
501         inputVariable->setValue(value);
502     }
503 
addInputVariable(InputVariable * inputVariable)504     void Engine::addInputVariable(InputVariable* inputVariable) {
505         inputVariables().push_back(inputVariable);
506     }
507 
setInputVariable(InputVariable * inputVariable,std::size_t index)508     InputVariable* Engine::setInputVariable(InputVariable* inputVariable, std::size_t index) {
509         InputVariable* result = inputVariables().at(index);
510         inputVariables().at(index) = inputVariable;
511         return result;
512     }
513 
insertInputVariable(InputVariable * inputVariable,std::size_t index)514     void Engine::insertInputVariable(InputVariable* inputVariable, std::size_t index) {
515         inputVariables().insert(inputVariables().begin() + index, inputVariable);
516     }
517 
getInputVariable(std::size_t index) const518     InputVariable* Engine::getInputVariable(std::size_t index) const {
519         return inputVariables().at(index);
520     }
521 
getInputVariable(const std::string & name) const522     InputVariable* Engine::getInputVariable(const std::string& name) const {
523         for (std::size_t i = 0; i < inputVariables().size(); ++i) {
524             if (inputVariables().at(i)->getName() == name)
525                 return inputVariables().at(i);
526         }
527         throw Exception("[engine error] input variable <" + name + "> not found", FL_AT);
528     }
529 
hasInputVariable(const std::string & name) const530     bool Engine::hasInputVariable(const std::string& name) const {
531         for (std::size_t i = 0; i < inputVariables().size(); ++i) {
532             if (inputVariables().at(i)->getName() == name)
533                 return true;
534         }
535         return false;
536     }
537 
removeInputVariable(std::size_t index)538     InputVariable* Engine::removeInputVariable(std::size_t index) {
539         InputVariable* result = inputVariables().at(index);
540         inputVariables().erase(inputVariables().begin() + index);
541         return result;
542     }
543 
removeInputVariable(const std::string & name)544     InputVariable* Engine::removeInputVariable(const std::string& name) {
545         for (std::size_t i = 0; i < inputVariables().size(); ++i) {
546             if (inputVariables().at(i)->getName() == name) {
547                 InputVariable* result = inputVariables().at(i);
548                 inputVariables().erase(inputVariables().begin() + i);
549                 return result;
550             }
551         }
552         throw Exception("[engine error] input variable <" + name + "> not found", FL_AT);
553     }
554 
numberOfInputVariables() const555     std::size_t Engine::numberOfInputVariables() const {
556         return inputVariables().size();
557     }
558 
inputVariables() const559     const std::vector<InputVariable*>& Engine::inputVariables() const {
560         return this->_inputVariables;
561     }
562 
setInputVariables(const std::vector<InputVariable * > & inputVariables)563     void Engine::setInputVariables(const std::vector<InputVariable*>& inputVariables) {
564         this->_inputVariables = inputVariables;
565     }
566 
inputVariables()567     std::vector<InputVariable*>& Engine::inputVariables() {
568         return this->_inputVariables;
569     }
570 
571     /**
572      * Operations for OutputVariables
573      */
getOutputValue(const std::string & name)574     scalar Engine::getOutputValue(const std::string& name) {
575         OutputVariable* outputVariable = getOutputVariable(name);
576         return outputVariable->getValue();
577     }
578 
addOutputVariable(OutputVariable * outputVariable)579     void Engine::addOutputVariable(OutputVariable* outputVariable) {
580         outputVariables().push_back(outputVariable);
581     }
582 
setOutputVariable(OutputVariable * outputVariable,std::size_t index)583     OutputVariable* Engine::setOutputVariable(OutputVariable* outputVariable, std::size_t index) {
584         OutputVariable* result = outputVariables().at(index);
585         outputVariables().at(index) = outputVariable;
586         return result;
587     }
588 
insertOutputVariable(OutputVariable * outputVariable,std::size_t index)589     void Engine::insertOutputVariable(OutputVariable* outputVariable, std::size_t index) {
590         outputVariables().insert(outputVariables().begin() + index, outputVariable);
591     }
592 
getOutputVariable(std::size_t index) const593     OutputVariable* Engine::getOutputVariable(std::size_t index) const {
594         return outputVariables().at(index);
595     }
596 
getOutputVariable(const std::string & name) const597     OutputVariable* Engine::getOutputVariable(const std::string& name) const {
598         for (std::size_t i = 0; i < outputVariables().size(); ++i) {
599             if (outputVariables().at(i)->getName() == name)
600                 return outputVariables().at(i);
601         }
602         throw Exception("[engine error] output variable <" + name + "> not found", FL_AT);
603     }
604 
hasOutputVariable(const std::string & name) const605     bool Engine::hasOutputVariable(const std::string& name) const {
606         for (std::size_t i = 0; i < outputVariables().size(); ++i) {
607             if (outputVariables().at(i)->getName() == name)
608                 return true;
609         }
610         return false;
611     }
612 
removeOutputVariable(std::size_t index)613     OutputVariable* Engine::removeOutputVariable(std::size_t index) {
614         OutputVariable* result = outputVariables().at(index);
615         outputVariables().erase(outputVariables().begin() + index);
616         return result;
617     }
618 
removeOutputVariable(const std::string & name)619     OutputVariable* Engine::removeOutputVariable(const std::string& name) {
620         for (std::size_t i = 0; i < outputVariables().size(); ++i) {
621             if (outputVariables().at(i)->getName() == name) {
622                 OutputVariable* result = outputVariables().at(i);
623                 outputVariables().erase(outputVariables().begin() + i);
624                 return result;
625             }
626         }
627         throw Exception("[engine error] output variable <" + name + "> not found", FL_AT);
628     }
629 
numberOfOutputVariables() const630     std::size_t Engine::numberOfOutputVariables() const {
631         return outputVariables().size();
632     }
633 
outputVariables() const634     const std::vector<OutputVariable*>& Engine::outputVariables() const {
635         return this->_outputVariables;
636     }
637 
setOutputVariables(const std::vector<OutputVariable * > & outputVariables)638     void Engine::setOutputVariables(const std::vector<OutputVariable*>& outputVariables) {
639         this->_outputVariables = outputVariables;
640     }
641 
outputVariables()642     std::vector<OutputVariable*>& Engine::outputVariables() {
643         return this->_outputVariables;
644     }
645 
646     /**
647      * Operations for iterable datatype _ruleblocks
648      */
addRuleBlock(RuleBlock * ruleblock)649     void Engine::addRuleBlock(RuleBlock* ruleblock) {
650         ruleBlocks().push_back(ruleblock);
651     }
652 
setRuleBlock(RuleBlock * ruleBlock,std::size_t index)653     RuleBlock* Engine::setRuleBlock(RuleBlock* ruleBlock, std::size_t index) {
654         RuleBlock* result = ruleBlocks().at(index);
655         ruleBlocks().at(index) = ruleBlock;
656         return result;
657     }
658 
insertRuleBlock(RuleBlock * ruleblock,std::size_t index)659     void Engine::insertRuleBlock(RuleBlock* ruleblock, std::size_t index) {
660         ruleBlocks().insert(ruleBlocks().begin() + index, ruleblock);
661     }
662 
getRuleBlock(std::size_t index) const663     RuleBlock* Engine::getRuleBlock(std::size_t index) const {
664         return ruleBlocks().at(index);
665     }
666 
getRuleBlock(const std::string & name) const667     RuleBlock* Engine::getRuleBlock(const std::string& name) const {
668         for (std::size_t i = 0; i < ruleBlocks().size(); ++i) {
669             if (ruleBlocks().at(i)->getName() == name)
670                 return ruleBlocks().at(i);
671         }
672         throw Exception("[engine error] rule block <" + name + "> not found", FL_AT);
673     }
674 
hasRuleBlock(const std::string & name) const675     bool Engine::hasRuleBlock(const std::string& name) const {
676         for (std::size_t i = 0; i < ruleBlocks().size(); ++i) {
677             if (ruleBlocks().at(i)->getName() == name)
678                 return true;
679         }
680         return false;
681     }
682 
removeRuleBlock(std::size_t index)683     RuleBlock* Engine::removeRuleBlock(std::size_t index) {
684         RuleBlock* result = ruleBlocks().at(index);
685         ruleBlocks().erase(ruleBlocks().begin() + index);
686         return result;
687     }
688 
removeRuleBlock(const std::string & name)689     RuleBlock* Engine::removeRuleBlock(const std::string& name) {
690         for (std::size_t i = 0; i < ruleBlocks().size(); ++i) {
691             if (ruleBlocks().at(i)->getName() == name) {
692                 RuleBlock* result = ruleBlocks().at(i);
693                 ruleBlocks().erase(ruleBlocks().begin() + i);
694                 return result;
695             }
696         }
697         throw Exception("[engine error] rule block <" + name + "> not found", FL_AT);
698     }
699 
numberOfRuleBlocks() const700     std::size_t Engine::numberOfRuleBlocks() const {
701         return ruleBlocks().size();
702     }
703 
ruleBlocks() const704     const std::vector<RuleBlock*>& Engine::ruleBlocks() const {
705         return this->_ruleBlocks;
706     }
707 
setRuleBlocks(const std::vector<RuleBlock * > & ruleBlocks)708     void Engine::setRuleBlocks(const std::vector<RuleBlock*>& ruleBlocks) {
709         this->_ruleBlocks = ruleBlocks;
710     }
711 
ruleBlocks()712     std::vector<RuleBlock*>& Engine::ruleBlocks() {
713         return this->_ruleBlocks;
714     }
715 
716 }
717