/*
Author: Juan Rada-Vilela, Ph.D.
Copyright (C) 2010-2014 FuzzyLite Limited
All rights reserved
This file is part of fuzzylite.
fuzzylite is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
fuzzylite is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
for more details.
You should have received a copy of the GNU Lesser General Public License
along with fuzzylite. If not, see .
fuzzyliteâ„¢ is a trademark of FuzzyLite Limited.
*/
#include "fl/Engine.h"
#include "fl/defuzzifier/WeightedAverage.h"
#include "fl/defuzzifier/WeightedSum.h"
#include "fl/factory/DefuzzifierFactory.h"
#include "fl/factory/FactoryManager.h"
#include "fl/factory/SNormFactory.h"
#include "fl/factory/TNormFactory.h"
#include "fl/hedge/Hedge.h"
#include "fl/imex/FllExporter.h"
#include "fl/norm/t/AlgebraicProduct.h"
#include "fl/rule/Consequent.h"
#include "fl/rule/Expression.h"
#include "fl/rule/Rule.h"
#include "fl/rule/RuleBlock.h"
#include "fl/term/Accumulated.h"
#include "fl/term/Constant.h"
#include "fl/term/Linear.h"
#include "fl/term/Function.h"
#include "fl/term/Ramp.h"
#include "fl/term/Sigmoid.h"
#include "fl/term/SShape.h"
#include "fl/term/ZShape.h"
#include "fl/variable/InputVariable.h"
#include "fl/variable/OutputVariable.h"
namespace fl {
Engine::Engine(const std::string& name) : _name(name) {
}
Engine::Engine(const Engine& other) : _name("") {
copyFrom(other);
}
Engine& Engine::operator=(const Engine& other) {
if (this != &other) {
for (std::size_t i = 0; i < _ruleblocks.size(); ++i)
delete _ruleblocks.at(i);
_ruleblocks.clear();
for (std::size_t i = 0; i < _outputVariables.size(); ++i)
delete _outputVariables.at(i);
_outputVariables.clear();
for (std::size_t i = 0; i < _inputVariables.size(); ++i)
delete _inputVariables.at(i);
_inputVariables.clear();
copyFrom(other);
}
return *this;
}
void Engine::copyFrom(const Engine& other) {
_name = other._name;
for (std::size_t i = 0; i < other._inputVariables.size(); ++i)
_inputVariables.push_back(new InputVariable(*other._inputVariables.at(i)));
for (std::size_t i = 0; i < other._outputVariables.size(); ++i)
_outputVariables.push_back(new OutputVariable(*other._outputVariables.at(i)));
updateReferences();
for (std::size_t i = 0; i < other._ruleblocks.size(); ++i) {
RuleBlock* ruleBlock = new RuleBlock(*other._ruleblocks.at(i));
try {
ruleBlock->loadRules(this);
} catch (...) {
}
_ruleblocks.push_back(ruleBlock);
}
}
void Engine::updateReferences() const {
std::vector myVariables = variables();
for (std::size_t i = 0; i < myVariables.size(); ++i) {
Variable* variable = myVariables.at(i);
for (int t = 0; t < variable->numberOfTerms(); ++t) {
Term::updateReference(variable->getTerm(t), this);
}
}
}
Engine::~Engine() {
for (std::size_t i = 0; i < _ruleblocks.size(); ++i) delete _ruleblocks.at(i);
for (std::size_t i = 0; i < _outputVariables.size(); ++i) delete _outputVariables.at(i);
for (std::size_t i = 0; i < _inputVariables.size(); ++i) delete _inputVariables.at(i);
}
void Engine::configure(const std::string& conjunctionT, const std::string& disjunctionS,
const std::string& activationT, const std::string& accumulationS,
const std::string& defuzzifierName, int resolution) {
TNormFactory* tnormFactory = FactoryManager::instance()->tnorm();
SNormFactory* snormFactory = FactoryManager::instance()->snorm();
DefuzzifierFactory* defuzzFactory = FactoryManager::instance()->defuzzifier();
TNorm* conjunction = tnormFactory->constructObject(conjunctionT);
SNorm* disjunction = snormFactory->constructObject(disjunctionS);
TNorm* activation = tnormFactory->constructObject(activationT);
SNorm* accumulation = snormFactory->constructObject(accumulationS);
Defuzzifier* defuzzifier = defuzzFactory->constructObject(defuzzifierName);
IntegralDefuzzifier* integralDefuzzifier = dynamic_cast (defuzzifier);
if (integralDefuzzifier) integralDefuzzifier->setResolution(resolution);
configure(conjunction, disjunction, activation, accumulation, defuzzifier);
}
void Engine::configure(TNorm* conjunction, SNorm* disjunction,
TNorm* activation, SNorm* accumulation, Defuzzifier* defuzzifier) {
for (std::size_t i = 0; i < _ruleblocks.size(); ++i) {
_ruleblocks.at(i)->setConjunction(conjunction ? conjunction->clone() : fl::null);
_ruleblocks.at(i)->setDisjunction(disjunction ? disjunction->clone() : fl::null);
_ruleblocks.at(i)->setActivation(activation ? activation->clone() : fl::null);
}
for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
_outputVariables.at(i)->setDefuzzifier(defuzzifier ? defuzzifier->clone() : fl::null);
_outputVariables.at(i)->fuzzyOutput()->setAccumulation(
accumulation ? accumulation->clone() : fl::null);
}
if (defuzzifier) delete defuzzifier;
if (accumulation) delete accumulation;
if (activation) delete activation;
if (disjunction) delete disjunction;
if (conjunction) delete conjunction;
}
bool Engine::isReady(std::string* status) const {
std::ostringstream ss;
if (_inputVariables.empty()) {
ss << "- Engine <" << _name << "> has no input variables\n";
}
for (std::size_t i = 0; i < _inputVariables.size(); ++i) {
InputVariable* inputVariable = _inputVariables.at(i);
if (not inputVariable) {
ss << "- Engine <" << _name << "> has a fl::null input variable at index <" << i << ">\n";
} else if (inputVariable->terms().empty()) {
//ignore because sometimes inputs can be empty: takagi-sugeno/matlab/slcpp1.fis
// ss << "- Input variable <" << _inputVariables.at(i)->getName() << ">"
// << " has no terms\n";
}
}
if (_outputVariables.empty()) {
ss << "- Engine <" << _name << "> has no output variables\n";
}
for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
OutputVariable* outputVariable = _outputVariables.at(i);
if (not outputVariable) {
ss << "- Engine <" << _name << "> has a fl::null output variable at index <" << i << ">\n";
} else {
if (outputVariable->terms().empty()) {
ss << "- Output variable <" << outputVariable->getName() << ">"
<< " has no terms\n";
}
Defuzzifier* defuzzifier = outputVariable->getDefuzzifier();
if (not defuzzifier) {
ss << "- Output variable <" << outputVariable->getName() << ">"
<< " has no defuzzifier\n";
}
SNorm* accumulation = outputVariable->fuzzyOutput()->getAccumulation();
if (not accumulation and dynamic_cast (defuzzifier)) {
ss << "- Output variable <" << outputVariable->getName() << ">"
<< " has no accumulation operator\n";
}
}
}
if (_ruleblocks.empty()) {
ss << "- Engine <" << _name << "> has no rule blocks\n";
}
for (std::size_t i = 0; i < _ruleblocks.size(); ++i) {
RuleBlock* ruleblock = _ruleblocks.at(i);
if (not ruleblock) {
ss << "- Engine <" << _name << "> has a fl::null rule block at index <" << i << ">\n";
} else {
if (ruleblock->rules().empty()) {
ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has no rules\n";
}
int requiresConjunction = 0;
int requiresDisjunction = 0;
int requiresActivation = 0;
for (int r = 0; r < ruleblock->numberOfRules(); ++r) {
Rule* rule = ruleblock->getRule(r);
if (not rule) {
ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName()
<< "> has a fl::null rule at index <" << r << ">\n";
} else {
std::size_t thenIndex = rule->getText().find(" " + Rule::thenKeyword() + " ");
std::size_t andIndex = rule->getText().find(" " + Rule::andKeyword() + " ");
std::size_t orIndex = rule->getText().find(" " + Rule::orKeyword() + " ");
if (andIndex != std::string::npos and andIndex < thenIndex) {
++requiresConjunction;
}
if (orIndex != std::string::npos and orIndex < thenIndex) {
++requiresDisjunction;
}
if (rule->isLoaded()) {
Consequent* consequent = rule->getConsequent();
for (std::size_t c = 0; c < consequent->conclusions().size(); ++c) {
Proposition* proposition = consequent->conclusions().at(c);
const OutputVariable* outputVariable =
dynamic_cast (proposition->variable);
if (outputVariable and dynamic_cast (outputVariable->getDefuzzifier())) {
++requiresActivation;
break;
}
}
}
}
}
const TNorm* conjunction = ruleblock->getConjunction();
if (requiresConjunction > 0 and not conjunction) {
ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has no conjunction operator\n";
ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has "
<< requiresConjunction << " rules that require conjunction operator\n";
}
const SNorm* disjunction = ruleblock->getDisjunction();
if (requiresDisjunction > 0 and not disjunction) {
ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has no disjunction operator\n";
ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has "
<< requiresDisjunction << " rules that require disjunction operator\n";
}
const TNorm* activation = ruleblock->getActivation();
if (requiresActivation > 0 and not activation) {
ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has no activation operator\n";
ss << "- Rule block " << (i + 1) << " <" << ruleblock->getName() << "> has "
<< requiresActivation << " rules that require activation operator\n";
}
}
}
if (status) *status = ss.str();
return ss.str().empty();
}
void Engine::restart() {
for (std::size_t i = 0; i < _inputVariables.size(); ++i) {
_inputVariables.at(i)->setInputValue(fl::nan);
}
for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
_outputVariables.at(i)->clear();
}
}
void Engine::process() {
for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
_outputVariables.at(i)->fuzzyOutput()->clear();
}
FL_DEBUG_BEGIN;
FL_DBG("===============");
FL_DBG("CURRENT INPUTS:");
for (std::size_t i = 0; i < _inputVariables.size(); ++i) {
InputVariable* inputVariable = _inputVariables.at(i);
scalar inputValue = inputVariable->getInputValue();
if (inputVariable->isEnabled()) {
FL_DBG(inputVariable->getName() << ".input = " << Op::str(inputValue));
FL_DBG(inputVariable->getName() << ".fuzzy = " << inputVariable->fuzzify(inputValue));
} else {
FL_DBG(inputVariable->getName() << ".enabled = false");
}
}
FL_DEBUG_END;
for (std::size_t i = 0; i < _ruleblocks.size(); ++i) {
RuleBlock* ruleBlock = _ruleblocks.at(i);
if (ruleBlock->isEnabled()) {
ruleBlock->activate();
}
}
for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
_outputVariables.at(i)->defuzzify();
}
FL_DEBUG_BEGIN;
FL_DBG("===============");
FL_DBG("CURRENT OUTPUTS:");
for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
OutputVariable* outputVariable = _outputVariables.at(i);
if (outputVariable->isEnabled()) {
FL_DBG(outputVariable->getName() << ".default = "
<< outputVariable->getDefaultValue());
FL_DBG(outputVariable->getName() << ".lockValueInRange = "
<< outputVariable->isLockedOutputValueInRange());
FL_DBG(outputVariable->getName() << ".lockPreviousValue= "
<< outputVariable->isLockedPreviousOutputValue());
scalar output = outputVariable->getOutputValue();
FL_DBG(outputVariable->getName() << ".output = " << output);
FL_DBG(outputVariable->getName() << ".fuzzy = " <<
outputVariable->fuzzify(output));
FL_DBG(outputVariable->fuzzyOutput()->toString());
} else {
FL_DBG(outputVariable->getName() << ".enabled = false");
}
}
FL_DBG("==============");
FL_DEBUG_END;
}
void Engine::setName(const std::string& name) {
this->_name = name;
}
std::string Engine::getName() const {
return this->_name;
}
std::string Engine::toString() const {
return FllExporter().toString(this);
}
Engine::Type Engine::type(std::string* name, std::string* reason) const {
if (_outputVariables.empty()) {
if (name) *name = "Unknown";
if (reason) *reason = "- Engine has no output variables";
return Engine::Unknown;
}
//Mamdani
bool mamdani = true;
for (std::size_t i = 0; mamdani and i < _outputVariables.size(); ++i) {
OutputVariable* outputVariable = _outputVariables.at(i);
//Defuzzifier must be integral
mamdani = mamdani and dynamic_cast (outputVariable->getDefuzzifier());
}
//Larsen
bool larsen = mamdani and not _ruleblocks.empty();
//Larsen is Mamdani with AlgebraicProduct as Activation
if (mamdani) {
for (std::size_t i = 0; larsen and i < _ruleblocks.size(); ++i) {
RuleBlock* ruleBlock = _ruleblocks.at(i);
larsen = larsen and dynamic_cast (ruleBlock->getActivation());
}
}
if (larsen) {
if (name) *name = "Larsen";
if (reason) *reason = "- Output variables have integral defuzzifiers\n"
"- Rule blocks activate using the algebraic product T-Norm";
return Engine::Larsen;
}
if (mamdani) {
if (name) *name = "Mamdani";
if (reason) *reason = "-Output variables have integral defuzzifiers";
return Engine::Mamdani;
}
//Else, keep checking
//TakagiSugeno
bool takagiSugeno = true;
for (std::size_t i = 0; takagiSugeno and i < _outputVariables.size(); ++i) {
OutputVariable* outputVariable = _outputVariables.at(i);
//Defuzzifier is Weighted
WeightedDefuzzifier* weightedDefuzzifier =
dynamic_cast (outputVariable->getDefuzzifier());
takagiSugeno = takagiSugeno and weightedDefuzzifier and
(weightedDefuzzifier->getType() == WeightedDefuzzifier::Automatic or
weightedDefuzzifier->getType() == WeightedDefuzzifier::TakagiSugeno);
if (takagiSugeno) {
//Takagi-Sugeno has only Constant, Linear or Function terms
for (int t = 0; takagiSugeno and t < outputVariable->numberOfTerms(); ++t) {
Term* term = outputVariable->getTerm(t);
takagiSugeno = takagiSugeno and
weightedDefuzzifier->inferType(term) == WeightedDefuzzifier::TakagiSugeno;
}
}
}
if (takagiSugeno) {
if (name) *name = "Takagi-Sugeno";
if (reason) *reason = "- Output variables have weighted defuzzifiers\n"
"- Output variables have constant, linear or function terms";
return Engine::TakagiSugeno;
}
//Tsukamoto
bool tsukamoto = true;
for (std::size_t i = 0; tsukamoto and i < _outputVariables.size(); ++i) {
OutputVariable* outputVariable = _outputVariables.at(i);
//Defuzzifier is Weighted
WeightedDefuzzifier* weightedDefuzzifier =
dynamic_cast (outputVariable->getDefuzzifier());
tsukamoto = tsukamoto and weightedDefuzzifier and
(weightedDefuzzifier->getType() == WeightedDefuzzifier::Automatic or
weightedDefuzzifier->getType() == WeightedDefuzzifier::Tsukamoto);
if (tsukamoto) {
//Tsukamoto has only monotonic terms: Concave, Ramp, Sigmoid, SShape, or ZShape
for (int t = 0; tsukamoto and t < outputVariable->numberOfTerms(); ++t) {
Term* term = outputVariable->getTerm(t);
tsukamoto = tsukamoto and weightedDefuzzifier->isMonotonic(term);
}
}
}
if (tsukamoto) {
if (name) *name = "Tsukamoto";
if (reason) *reason = "- Output variables have weighted defuzzifiers\n"
"- Output variables only have monotonic terms";
return Engine::Tsukamoto;
}
//Inverse Tsukamoto
bool inverseTsukamoto = true;
for (std::size_t i = 0; inverseTsukamoto and i < _outputVariables.size(); ++i) {
OutputVariable* outputVariable = _outputVariables.at(i);
//Defuzzifier cannot be integral
WeightedDefuzzifier* weightedDefuzzifier =
dynamic_cast (outputVariable->getDefuzzifier());
inverseTsukamoto = inverseTsukamoto and weightedDefuzzifier;
}
if (inverseTsukamoto) {
if (name) *name = "Inverse Tsukamoto";
if (reason) *reason = "- Output variables have weighted defuzzifiers\n"
"- Output variables do not only have constant, linear or function terms\n"
"- Output variables do not only have monotonic terms";
return Engine::InverseTsukamoto;
}
bool hybrid = true;
for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
OutputVariable* outputVariable = _outputVariables.at(i);
//Output variables have non-fl::null defuzzifiers
hybrid = hybrid and outputVariable->getDefuzzifier();
}
if (hybrid) {
if (name) *name = "Hybrid";
if (reason) *reason = "- Output variables have different defuzzifiers";
return Engine::Hybrid;
}
if (name) *name = "Unknown";
if (reason) *reason = "- There are output variables without a defuzzifier";
return Engine::Unknown;
}
Engine* Engine::clone() const {
return new Engine(*this);
}
std::vector Engine::variables() const {
std::vector result;
result.reserve(_inputVariables.size() + _outputVariables.size());
result.insert(result.end(), _inputVariables.begin(), _inputVariables.end());
result.insert(result.end(), _outputVariables.begin(), _outputVariables.end());
return result;
}
/**
* Operations for InputVariables
*/
void Engine::setInputValue(const std::string& name, scalar value) {
InputVariable* inputVariable = getInputVariable(name);
inputVariable->setInputValue(value);
}
void Engine::addInputVariable(InputVariable* inputVariable) {
this->_inputVariables.push_back(inputVariable);
}
InputVariable* Engine::setInputVariable(InputVariable* inputVariable, int index) {
InputVariable* result = this->_inputVariables.at(index);
this->_inputVariables.at(index) = inputVariable;
return result;
}
void Engine::insertInputVariable(InputVariable* inputVariable, int index) {
this->_inputVariables.insert(this->_inputVariables.begin() + index,
inputVariable);
}
InputVariable* Engine::getInputVariable(int index) const {
return this->_inputVariables.at(index);
}
InputVariable* Engine::getInputVariable(const std::string& name) const {
for (std::size_t i = 0; i < _inputVariables.size(); ++i) {
if (_inputVariables.at(i)->getName() == name)
return _inputVariables.at(i);
}
throw fl::Exception("[engine error] input variable <" + name + "> not found", FL_AT);
}
bool Engine::hasInputVariable(const std::string& name) const {
for (std::size_t i = 0; i < _inputVariables.size(); ++i) {
if (_inputVariables.at(i)->getName() == name)
return true;
}
return false;
}
InputVariable* Engine::removeInputVariable(int index) {
InputVariable* result = this->_inputVariables.at(index);
this->_inputVariables.erase(this->_inputVariables.begin() + index);
return result;
}
InputVariable* Engine::removeInputVariable(const std::string& name) {
for (std::size_t i = 0; i < _inputVariables.size(); ++i) {
if (_inputVariables.at(i)->getName() == name) {
InputVariable* result = this->_inputVariables.at(i);
this->_inputVariables.erase(this->_inputVariables.begin() + i);
return result;
}
}
throw fl::Exception("[engine error] input variable <" + name + "> not found", FL_AT);
}
int Engine::numberOfInputVariables() const {
return this->_inputVariables.size();
}
const std::vector& Engine::inputVariables() const {
return this->_inputVariables;
}
void Engine::setInputVariables(const std::vector& inputVariables) {
this->_inputVariables = inputVariables;
}
std::vector& Engine::inputVariables() {
return this->_inputVariables;
}
/**
* Operations for OutputVariables
*/
scalar Engine::getOutputValue(const std::string& name) {
OutputVariable* outputVariable = getOutputVariable(name);
return outputVariable->getOutputValue();
}
void Engine::addOutputVariable(OutputVariable* outputVariable) {
this->_outputVariables.push_back(outputVariable);
}
OutputVariable* Engine::setOutputVariable(OutputVariable* outputVariable, int index) {
OutputVariable* result = this->_outputVariables.at(index);
this->_outputVariables.at(index) = outputVariable;
return result;
}
void Engine::insertOutputVariable(OutputVariable* outputVariable, int index) {
this->_outputVariables.insert(this->_outputVariables.begin() + index,
outputVariable);
}
OutputVariable* Engine::getOutputVariable(int index) const {
return this->_outputVariables.at(index);
}
OutputVariable* Engine::getOutputVariable(const std::string& name) const {
for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
if (_outputVariables.at(i)->getName() == name)
return _outputVariables.at(i);
}
throw fl::Exception("[engine error] output variable <" + name + "> not found", FL_AT);
}
bool Engine::hasOutputVariable(const std::string& name) const {
for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
if (_outputVariables.at(i)->getName() == name)
return true;
}
return false;
}
OutputVariable* Engine::removeOutputVariable(int index) {
OutputVariable* result = this->_outputVariables.at(index);
this->_outputVariables.erase(this->_outputVariables.begin() + index);
return result;
}
OutputVariable* Engine::removeOutputVariable(const std::string& name) {
for (std::size_t i = 0; i < _outputVariables.size(); ++i) {
if (_outputVariables.at(i)->getName() == name) {
OutputVariable* result = this->_outputVariables.at(i);
this->_outputVariables.erase(this->_outputVariables.begin() + i);
return result;
}
}
throw fl::Exception("[engine error] output variable <" + name + "> not found", FL_AT);
}
int Engine::numberOfOutputVariables() const {
return this->_outputVariables.size();
}
const std::vector& Engine::outputVariables() const {
return this->_outputVariables;
}
void Engine::setOutputVariables(const std::vector& outputVariables) {
this->_outputVariables = outputVariables;
}
std::vector& Engine::outputVariables() {
return this->_outputVariables;
}
/**
* Operations for iterable datatype _ruleblocks
*/
void Engine::addRuleBlock(RuleBlock* ruleblock) {
this->_ruleblocks.push_back(ruleblock);
}
RuleBlock* Engine::setRuleBlock(RuleBlock* ruleBlock, int index) {
RuleBlock* result = this->_ruleblocks.at(index);
this->_ruleblocks.at(index) = ruleBlock;
return result;
}
void Engine::insertRuleBlock(RuleBlock* ruleblock, int index) {
this->_ruleblocks.insert(this->_ruleblocks.begin() + index, ruleblock);
}
RuleBlock* Engine::getRuleBlock(int index) const {
return this->_ruleblocks.at(index);
}
RuleBlock* Engine::getRuleBlock(const std::string& name) const {
for (std::size_t i = 0; i < _ruleblocks.size(); ++i) {
if (_ruleblocks.at(i)->getName() == name)
return _ruleblocks.at(i);
}
throw fl::Exception("[engine error] rule block <" + name + "> not found", FL_AT);
}
bool Engine::hasRuleBlock(const std::string& name) const {
for (std::size_t i = 0; i < _ruleblocks.size(); ++i) {
if (_ruleblocks.at(i)->getName() == name)
return true;
}
return false;
}
RuleBlock* Engine::removeRuleBlock(int index) {
RuleBlock* result = this->_ruleblocks.at(index);
this->_ruleblocks.erase(this->_ruleblocks.begin() + index);
return result;
}
RuleBlock* Engine::removeRuleBlock(const std::string& name) {
for (std::size_t i = 0; i < _ruleblocks.size(); ++i) {
if (_ruleblocks.at(i)->getName() == name) {
RuleBlock* result = this->_ruleblocks.at(i);
this->_ruleblocks.erase(this->_ruleblocks.begin() + i);
return result;
}
}
throw fl::Exception("[engine error] rule block <" + name + "> not found", FL_AT);
}
int Engine::numberOfRuleBlocks() const {
return this->_ruleblocks.size();
}
const std::vector& Engine::ruleBlocks() const {
return this->_ruleblocks;
}
void Engine::setRuleBlocks(const std::vector& ruleBlocks) {
this->_ruleblocks = ruleBlocks;
}
std::vector& Engine::ruleBlocks() {
return this->_ruleblocks;
}
}