333 строки
12 КиБ
MQL5
333 строки
12 КиБ
MQL5
//+------------------------------------------------------------------+
| |||
//| genericfuzzysystem.mqh |
| |||
//| Copyright 2000-2025, MetaQuotes Ltd. |
| |||
//| https://www.mql5.com |
| |||
//+------------------------------------------------------------------+
| |||
//| Implementation of Fuzzy library in MetaQuotes Language 5 |
| |||
//| |
| |||
//| The features of the library include: |
| |||
//| - Create Mamdani fuzzy model |
| |||
//| - Create Sugeno fuzzy model |
| |||
//| - Normal membership function |
| |||
//| - Triangular membership function |
| |||
//| - Trapezoidal membership function |
| |||
//| - Constant membership function |
| |||
//| - Defuzzification method of center of gravity (COG) |
| |||
//| - Defuzzification method of bisector of area (BOA) |
| |||
//| - Defuzzification method of mean of maxima (MeOM) |
| |||
//| |
| |||
//| This file is free software; you can redistribute it and/or |
| |||
//| modify it under the terms of the GNU General Public License as |
| |||
//| published by the Free Software Foundation (www.fsf.org); either |
| |||
//| version 2 of the License, or (at your option) any later version. |
| |||
//| |
| |||
//| This program 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 General Public License for more details. |
| |||
//+------------------------------------------------------------------+
| |||
#include <Arrays\List.mqh>
| |||
#include <Arrays\ArrayObj.mqh>
| |||
#include "FuzzyRule.mqh"
| |||
#include "InferenceMethod.mqh"
| |||
#include "Dictionary.mqh"
| |||
//+------------------------------------------------------------------+
| |||
//| Purpose: Creating generic fuzzy system |
| |||
//+------------------------------------------------------------------+
| |||
//+------------------------------------------------------------------+
| |||
//| Common functionality of Mamdani and Sugeno fuzzy systems |
| |||
//+------------------------------------------------------------------+
| |||
class CGenericFuzzySystem
| |||
{
| |||
private:
| |||
CList *m_input; // List of input fuzzy variables
| |||
EnAndMethod m_and_method; // And method from InferenceMethod
| |||
EnOrMethod m_or_method; // Or method from InferenceMethod
| |||
| |||
protected:
| |||
CGenericFuzzySystem(void);
| |||
~CGenericFuzzySystem(void);
| |||
public:
| |||
//--- method gets the input linguistic variables
| |||
CList *Input(void) { return(m_input); }
| |||
//--- method gets or sets the type of "And method"
| |||
void AndMethod(EnAndMethod value) { m_and_method=value; }
| |||
EnAndMethod AndMethod(void) const { return (m_and_method); }
| |||
//--- method gets or sets the type of "Or method"
| |||
void OrMethod(EnOrMethod value) { m_or_method=value; }
| |||
EnOrMethod OrMethod(void) const { return (m_or_method); }
| |||
//--- method gets the varriable by name
| |||
CFuzzyVariable *InputByName(const string name);
| |||
//--- common steps of calculating
| |||
CList *Fuzzify(CList *inputValues);
| |||
protected:
| |||
double EvaluateCondition(ICondition *condition,CList *fuzzifiedInput);
| |||
double EvaluateConditionPair(const double cond1,const double cond2,OperatorType op);
| |||
private:
| |||
bool ValidateInputValues(CList *inputValues,string &msg);
| |||
};
| |||
//+------------------------------------------------------------------+
| |||
//| Constructor without parameters |
| |||
//+------------------------------------------------------------------+
| |||
CGenericFuzzySystem::CGenericFuzzySystem(void)
| |||
{
| |||
m_input=new CList;
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Destructor |
| |||
//+------------------------------------------------------------------+
| |||
CGenericFuzzySystem::~CGenericFuzzySystem(void)
| |||
{
| |||
if(CheckPointer(m_input)==POINTER_DYNAMIC)
| |||
{
| |||
delete m_input;
| |||
}
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Get input linguistic variable by its name |
| |||
//+------------------------------------------------------------------+
| |||
CFuzzyVariable *CGenericFuzzySystem::InputByName(const string name)
| |||
{
| |||
CList *result=CGenericFuzzySystem::Input();
| |||
for(int i=0; i<result.Total(); i++)
| |||
{
| |||
CFuzzyVariable *var=result.GetNodeAtIndex(i);
| |||
if(var.Name()==name)
| |||
{
| |||
//--- return fuzzy variable
| |||
return (var);
| |||
}
| |||
}
| |||
Print("The variable with that name is not found");
| |||
//--- return
| |||
return (NULL);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Fuzzify input |
| |||
//+------------------------------------------------------------------+
| |||
CList *CGenericFuzzySystem::Fuzzify(CList *inputValues)
| |||
{
| |||
//--- Validate input
| |||
string msg;
| |||
if(!ValidateInputValues(inputValues,msg))
| |||
{
| |||
Print(msg);
| |||
//--- return
| |||
return (NULL);
| |||
}
| |||
//--- Fill results list
| |||
CList *result=new CList;
| |||
for(int i=0; i<Input().Total(); i++)
| |||
{
| |||
CFuzzyVariable *var=Input().GetNodeAtIndex(i);
| |||
double value=NULL;
| |||
for(int k=0; k<inputValues.Total(); k++)
| |||
{
| |||
CDictionary_Obj_Double *p_vd=inputValues.GetNodeAtIndex(i);
| |||
CFuzzyVariable *v=p_vd.Key();
| |||
if(p_vd.Key()==var)
| |||
{
| |||
value=p_vd.Value();
| |||
break;
| |||
}
| |||
}
| |||
CList *resultForVar=new CList;
| |||
for(int j=0; j<var.Terms().Total(); j++)
| |||
{
| |||
CDictionary_Obj_Double *p_vd=new CDictionary_Obj_Double;
| |||
CFuzzyTerm *term=var.Terms().GetNodeAtIndex(j);
| |||
p_vd.SetAll(term,term.MembershipFunction().GetValue(value));
| |||
resultForVar.Add(p_vd);
| |||
}
| |||
CDictionary_Obj_Obj *p_vl=new CDictionary_Obj_Obj;
| |||
p_vl.SetAll(var,resultForVar);
| |||
result.Add(p_vl);
| |||
}
| |||
//--- return result
| |||
return (result);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Evaluate fuzzy condition (or conditions) |
| |||
//+------------------------------------------------------------------+
| |||
double CGenericFuzzySystem::EvaluateCondition(ICondition *condition,CList *fuzzifiedInput)
| |||
{
| |||
double result=0.0;
| |||
ICondition *IC;
| |||
if(condition.IsTypeOf(TYPE_CLASS_Conditions))
| |||
{
| |||
CConditions *conds=condition;
| |||
if(conds.ConditionsList().Total()==0)
| |||
{
| |||
Print("Inner exception.");
| |||
}
| |||
else if(conds.ConditionsList().Total()==1)
| |||
{
| |||
IC=conds.ConditionsList().GetNodeAtIndex(0);
| |||
result=EvaluateCondition(IC,fuzzifiedInput);
| |||
}
| |||
else
| |||
{
| |||
IC=conds.ConditionsList().GetNodeAtIndex(0);
| |||
result=EvaluateCondition(IC,fuzzifiedInput);
| |||
for(int i=1; i<conds.ConditionsList().Total(); i++)
| |||
{
| |||
IC=conds.ConditionsList().GetNodeAtIndex(i);
| |||
double cond2=EvaluateCondition(IC,fuzzifiedInput);;
| |||
result=EvaluateConditionPair(result,cond2,conds.Op());
| |||
}
| |||
}
| |||
if(conds.Not())
| |||
{
| |||
result=1.0-result;
| |||
}
| |||
//--- return result
| |||
return (result);
| |||
}
| |||
else if(condition.IsTypeOf(TYPE_CLASS_FuzzyCondition))
| |||
{
| |||
CFuzzyCondition *cond=condition;
| |||
CDictionary_Obj_Obj *p_vl;
| |||
CDictionary_Obj_Double *p_td;
| |||
for(int i=0; i<fuzzifiedInput.Total(); i++)
| |||
{
| |||
p_vl=fuzzifiedInput.GetNodeAtIndex(i);
| |||
if(p_vl.Key()==cond.Var())
| |||
{
| |||
CList *list=p_vl.Value();
| |||
for(int j=0; j<list.Total(); j++)
| |||
{
| |||
p_td=list.GetNodeAtIndex(j);
| |||
if(p_td.Key()==cond.Term())
| |||
{
| |||
break;
| |||
}
| |||
}
| |||
break;
| |||
}
| |||
}
| |||
result=p_td.Value();
| |||
switch(cond.Hedge())
| |||
{
| |||
case Slightly:
| |||
//--- Cube root
| |||
result=pow(result,1.0/3.0);
| |||
break;
| |||
case Somewhat:
| |||
result=sqrt(result);
| |||
break;
| |||
case Very:
| |||
result=result*result;
| |||
break;
| |||
case Extremely:
| |||
result=result*result*result;
| |||
break;
| |||
default:
| |||
break;
| |||
}
| |||
if(cond.Not())
| |||
{
| |||
result=1.0-result;
| |||
}
| |||
//--- return result
| |||
return (result);
| |||
}
| |||
else
| |||
{
| |||
Print("Internal error.");
| |||
//--- return
| |||
return (NULL);
| |||
}
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Evaluate fuzzy condition (or conditions) |
| |||
//+------------------------------------------------------------------+
| |||
double CGenericFuzzySystem::EvaluateConditionPair(const double cond1,const double cond2,OperatorType op)
| |||
{
| |||
if(op==And)
| |||
{
| |||
if(CGenericFuzzySystem::AndMethod()==MinAnd)
| |||
{
| |||
//--- return evaluate condition
| |||
return fmin(cond1, cond2);
| |||
}
| |||
else if(CGenericFuzzySystem::AndMethod()==ProductionAnd)
| |||
{
| |||
//--- return evaluate condition
| |||
return (cond1 * cond2);
| |||
}
| |||
else
| |||
{
| |||
Print("Internal error.");
| |||
//--- return
| |||
return(NULL);
| |||
}
| |||
}
| |||
else if(op==Or)
| |||
{
| |||
if(CGenericFuzzySystem::OrMethod()==MaxOr)
| |||
{
| |||
//--- return evaluate condition
| |||
return fmax(cond1, cond2);
| |||
}
| |||
else if(CGenericFuzzySystem::OrMethod()==ProbabilisticOr)
| |||
{
| |||
//--- return evaluate condition
| |||
return (cond1 + cond2 - cond1 * cond2);
| |||
}
| |||
else
| |||
{
| |||
Print("Internal error.");
| |||
//--- return
| |||
return (NULL);
| |||
}
| |||
}
| |||
else
| |||
{
| |||
Print("Internal error.");
| |||
//--- return
| |||
return (NULL);
| |||
}
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Validate input values |
| |||
//+------------------------------------------------------------------+
| |||
bool CGenericFuzzySystem::ValidateInputValues(CList *inputValues,string &msg)
| |||
{
| |||
msg=NULL;
| |||
if(inputValues.Total()!=Input().Total())
| |||
{
| |||
msg="Input values count is incorrect.";
| |||
//--- return false
| |||
return (false);
| |||
}
| |||
bool contain;
| |||
for(int i=0; i<Input().Total(); i++)
| |||
{
| |||
CFuzzyVariable *var=Input().GetNodeAtIndex(i);
| |||
contain=false;
| |||
for(int j=0; j<inputValues.Total();j++)
| |||
{
| |||
CDictionary_Obj_Double *p_vd=inputValues.GetNodeAtIndex(j);
| |||
if(p_vd.Key()==var)
| |||
{
| |||
contain=true;
| |||
double val=p_vd.Value();
| |||
if(val<var.Min() || val>var.Max())
| |||
{
| |||
msg=StringFormat("Value for the %s variable is out of range.",var.Name());
| |||
//--- return false
| |||
return (false);
| |||
}
| |||
}
| |||
}
| |||
if(contain==false)
| |||
{
| |||
msg=StringFormat("Value for the %s variable does not present.",var.Name());
| |||
//--- return false
| |||
return (false);
| |||
}
| |||
}
| |||
//--- return true
| |||
return (true);
| |||
}
| |||
//+------------------------------------------------------------------+
|