556 строки
22 КиБ
MQL5
556 строки
22 КиБ
MQL5
//+------------------------------------------------------------------+
| |||
//| mandanifuzzysystem.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\ArrayDouble.mqh>
| |||
#include "GenericFuzzySystem.mqh"
| |||
#include "InferenceMethod.mqh"
| |||
#include "RuleParser.mqh"
| |||
#include "FuzzyRule.mqh"
| |||
//+------------------------------------------------------------------+
| |||
//| Purpose: Creating Mamdani fuzzy system |
| |||
//+------------------------------------------------------------------+
| |||
//+------------------------------------------------------------------+
| |||
//| Mamdani fuzzy inference system |
| |||
//+------------------------------------------------------------------+
| |||
class CMamdaniFuzzySystem : public CGenericFuzzySystem
| |||
{
| |||
private:
| |||
CList* m_output; // List of fuzzy variable
| |||
CList* m_rules; // List of Mamdani fuzzy rule
| |||
ImplicationMethod m_impl_method; // Implication method
| |||
AggregationMethod m_aggr_method; // Aggregation method
| |||
DefuzzificationMethod m_defuzz_method; // Defuzzification method
| |||
| |||
public:
| |||
CMamdaniFuzzySystem(void);
| |||
~CMamdaniFuzzySystem(void);
| |||
//--- method gets the output linguistic variables
| |||
CList* Output(void) { return(m_output); }
| |||
//--- method gets the fuzzy rule
| |||
CList* Rules(void) { return(m_rules); }
| |||
//--- methods gets or sets the implication method
| |||
ImplicationMethod GetImplicationMethod(void) const { return (m_impl_method); }
| |||
void SetImplicationMethod(ImplicationMethod value) { m_impl_method=value; }
| |||
//--- methods gets or sets the aggregation method
| |||
AggregationMethod GetAggregationMethod(void) const { return (m_aggr_method); }
| |||
void SetAggregationMethod(AggregationMethod value) { m_aggr_method=value; }
| |||
//--- methods gets or sets the defuzzification method
| |||
DefuzzificationMethod GetDefuzzificationMethod(void) const { return (m_defuzz_method); }
| |||
void SetDefuzzificationMethod(DefuzzificationMethod value) { m_defuzz_method=value; }
| |||
//--- maethod gets the variable by name
| |||
CFuzzyVariable* OutputByName(const string name);
| |||
//--- create a new rule
| |||
CMamdaniFuzzyRule* EmptyRule(void);
| |||
//--- parse rule
| |||
CMamdaniFuzzyRule* ParseRule(const string rule);
| |||
//--- method for calculate result
| |||
CList* Calculate(CList *inputValues);
| |||
CList* EvaluateConditions(CList *fuzzifiedInput);
| |||
CList* Implicate(CList *conditions);
| |||
CList* Aggregate(CList *conclusions);
| |||
CList* Defuzzify(CList *fuzzyResult);
| |||
double Defuzzify(IMembershipFunction *mf,const double min,const double max);
| |||
};
| |||
//+------------------------------------------------------------------+
| |||
//| Constructor without parameters |
| |||
//+------------------------------------------------------------------+
| |||
CMamdaniFuzzySystem::CMamdaniFuzzySystem(void)
| |||
{
| |||
m_output = new CList;
| |||
m_rules = new CList;
| |||
m_impl_method = MinIpm; // Implication method default is Min
| |||
m_aggr_method = MaxAgg; // Aggregation method default is Max
| |||
m_defuzz_method = CentroidDef; // Defuzzification method default is Centroid
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Destructor |
| |||
//+------------------------------------------------------------------+
| |||
CMamdaniFuzzySystem::~CMamdaniFuzzySystem(void)
| |||
{
| |||
delete m_output;
| |||
delete m_rules;
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Get output linguistic variable by its name |
| |||
//+------------------------------------------------------------------+
| |||
CFuzzyVariable *CMamdaniFuzzySystem::OutputByName(const string name)
| |||
{
| |||
for(int i=0; i<m_output.Total(); i++)
| |||
{
| |||
CFuzzyVariable *var=m_output.GetNodeAtIndex(i);
| |||
if(var.Name()==name)
| |||
{
| |||
//--- return varriable
| |||
return (var);
| |||
}
| |||
}
| |||
Print("Variable with that name is not found");
| |||
//--- return
| |||
return (NULL);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Create new empty rule |
| |||
//+------------------------------------------------------------------+
| |||
CMamdaniFuzzyRule *CMamdaniFuzzySystem::EmptyRule()
| |||
{
| |||
//--- return empty rule
| |||
return new CMamdaniFuzzyRule();
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Parse rule from the string |
| |||
//+------------------------------------------------------------------+
| |||
CMamdaniFuzzyRule *CMamdaniFuzzySystem::ParseRule(const string rule)
| |||
{
| |||
//--- return Mamdani fuzzy rule
| |||
return CRuleParser::Parse(rule, EmptyRule(), Input(), Output());
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Calculate output values |
| |||
//+------------------------------------------------------------------+
| |||
CList *CMamdaniFuzzySystem::Calculate(CList *inputValues)
| |||
{
| |||
//--- There should be one rule as minimum
| |||
if(m_rules.Total()==0)
| |||
{
| |||
Print("There should be one rule as minimum.");
| |||
//--- return
| |||
return (NULL);
| |||
}
| |||
//--- Fuzzification step
| |||
CList *fuzzifiedInput=Fuzzify(inputValues);
| |||
//--- Evaluate the conditions
| |||
CList *evaluatedConditions=EvaluateConditions(fuzzifiedInput);
| |||
//--- Do implication for each rule
| |||
CList *implicatedConclusions=Implicate(evaluatedConditions);
| |||
//--- Aggrerate the results
| |||
CList *fuzzyResult=Aggregate(implicatedConclusions);
| |||
//--- Defuzzify the result
| |||
CList *result=Defuzzify(fuzzyResult);
| |||
//---
| |||
delete fuzzyResult;
| |||
for(int i=0; i<implicatedConclusions.Total(); i++)
| |||
{
| |||
CDictionary_Obj_Obj *pair=implicatedConclusions.GetNodeAtIndex(i);
| |||
CCompositeMembershipFunction *composite=pair.Value();
| |||
delete composite.MembershipFunctions().GetNodeAtIndex(0);
| |||
delete composite;
| |||
}
| |||
delete implicatedConclusions;
| |||
delete evaluatedConditions;
| |||
for(int i=0; i<fuzzifiedInput.Total(); i++)
| |||
{
| |||
CDictionary_Obj_Obj *pair=fuzzifiedInput.GetNodeAtIndex(i);
| |||
delete pair.Value();
| |||
}
| |||
delete fuzzifiedInput;
| |||
//--- return result
| |||
return (result);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Evaluate conditions |
| |||
//+------------------------------------------------------------------+
| |||
CList *CMamdaniFuzzySystem::EvaluateConditions(CList *fuzzifiedInput)
| |||
{
| |||
CList *result=new CList;
| |||
for(int i=0; i<Rules().Total(); i++)
| |||
{
| |||
CDictionary_Obj_Double *p_rd=new CDictionary_Obj_Double;
| |||
CMamdaniFuzzyRule *rule=Rules().GetNodeAtIndex(i);
| |||
p_rd.SetAll(rule,EvaluateCondition(rule.Condition(),fuzzifiedInput));
| |||
result.Add(p_rd);
| |||
}
| |||
//--- return result
| |||
return (result);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Implicate rule results |
| |||
//+------------------------------------------------------------------+
| |||
CList *CMamdaniFuzzySystem::Implicate(CList *conditions)
| |||
{
| |||
CList *conclusions=new CList;
| |||
for(int i=0; i<conditions.Total(); i++)
| |||
{
| |||
CDictionary_Obj_Double *p_rd=conditions.GetNodeAtIndex(i);
| |||
CMamdaniFuzzyRule *rule=p_rd.Key();
| |||
MfCompositionType compType;
| |||
switch(m_impl_method)
| |||
{
| |||
case MinIpm :
| |||
{
| |||
compType=MinMF;
| |||
break;
| |||
}
| |||
case ProductionImp :
| |||
{
| |||
compType=ProdMF;
| |||
break;
| |||
}
| |||
default :
| |||
{
| |||
Print("Internal error.");
| |||
//---
| |||
return (NULL);
| |||
}
| |||
}
| |||
CFuzzyTerm *val=rule.Conclusion().Term();
| |||
IMembershipFunction *first_fun=new CConstantMembershipFunction(p_rd.Value());
| |||
IMembershipFunction *second_fun=val.MembershipFunction();
| |||
CCompositeMembershipFunction *resultMF=new CCompositeMembershipFunction(compType,first_fun,second_fun);
| |||
CDictionary_Obj_Obj *p_rf=new CDictionary_Obj_Obj;
| |||
p_rf.SetAll(rule,resultMF);
| |||
conclusions.Add(p_rf);
| |||
}
| |||
//--- return conclusions
| |||
return (conclusions);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Aggregate results |
| |||
//+------------------------------------------------------------------+
| |||
CList *CMamdaniFuzzySystem::Aggregate(CList *conclusions)
| |||
{
| |||
CList *fuzzyResult=new CList;
| |||
for(int i=0; i<Output().Total(); i++)
| |||
{
| |||
CFuzzyVariable *var=Output().GetNodeAtIndex(i);
| |||
CList *mfList=new CList;
| |||
for(int j=0; j<conclusions.Total(); j++)
| |||
{
| |||
CDictionary_Obj_Obj *p_rf=conclusions.GetNodeAtIndex(j);
| |||
CMamdaniFuzzyRule *rule=p_rf.Key();
| |||
if(rule.Conclusion().Var()==var)
| |||
{
| |||
mfList.Add(p_rf.Value());
| |||
}
| |||
}
| |||
MfCompositionType composType;
| |||
switch(m_aggr_method)
| |||
{
| |||
case MaxAgg:
| |||
composType=MaxMF;
| |||
break;
| |||
case SumAgg:
| |||
composType=SumMF;
| |||
break;
| |||
default:
| |||
{
| |||
Print("Internal exception.");
| |||
//--- return
| |||
return (NULL);
| |||
}
| |||
}
| |||
CDictionary_Obj_Obj *p_vf=new CDictionary_Obj_Obj;
| |||
CCompositeMembershipFunction *func=new CCompositeMembershipFunction(composType,mfList);
| |||
p_vf.SetAll(var,func);
| |||
fuzzyResult.Add(p_vf);
| |||
}
| |||
//--- return result
| |||
return (fuzzyResult);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Calculate crisp result for each rule |
| |||
//+------------------------------------------------------------------+
| |||
CList *CMamdaniFuzzySystem::Defuzzify(CList *fuzzyResult)
| |||
{
| |||
CList *crispResult=new CList;
| |||
for(int i=0; i<fuzzyResult.Total(); i++)
| |||
{
| |||
CDictionary_Obj_Double *p_vd=new CDictionary_Obj_Double;
| |||
CDictionary_Obj_Obj *p_vf=fuzzyResult.GetNodeAtIndex(i);
| |||
CFuzzyVariable *var=p_vf.Key();
| |||
p_vd.SetAll(var,Defuzzify(p_vf.Value(),var.Min(),var.Max()));
| |||
crispResult.Add(p_vd);
| |||
}
| |||
//--- return result
| |||
return (crispResult);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Helpers |
| |||
//+------------------------------------------------------------------+
| |||
double CMamdaniFuzzySystem::Defuzzify(IMembershipFunction *mf,const double min,const double max)
| |||
{
| |||
if(m_defuzz_method==CentroidDef)
| |||
{
| |||
int k=50; // The function is divided into "k" steps
| |||
double step=(max-min)/k; // Calculate the step function
| |||
//+------------------------------------------------------------------+
| |||
//| Calculate a center of gravity as integral |
| |||
//+------------------------------------------------------------------+
| |||
double ptLeft=0.0;
| |||
double ptCenter= 0.0;
| |||
double ptRight = 0.0;
| |||
double valLeft=0.0;
| |||
double valCenter= 0.0;
| |||
double valRight = 0.0;
| |||
double val2Left=0.0;
| |||
double val2Center= 0.0;
| |||
double val2Right = 0.0;
| |||
double numerator=0.0;
| |||
double denominator=0.0;
| |||
for(int i=0; i<k; i++)
| |||
{
| |||
if(i==0)
| |||
{
| |||
ptRight=min;
| |||
valRight=mf.GetValue(ptRight);
| |||
val2Right=ptRight*valRight;
| |||
}
| |||
ptLeft=ptRight;
| |||
ptCenter= min+step *((double)i+0.5);
| |||
ptRight = min+step *(i+1);
| |||
valLeft=valRight;
| |||
valCenter= mf.GetValue(ptCenter);
| |||
valRight = mf.GetValue(ptRight);
| |||
val2Left=val2Right;
| |||
val2Center= ptCenter * valCenter;
| |||
val2Right = ptRight * valRight;
| |||
numerator+=step *(val2Left+4*val2Center+val2Right)/3.0;
| |||
denominator+=step *(valLeft+4*valCenter+valRight)/3.0;
| |||
}
| |||
delete mf;
| |||
if(denominator!=0)
| |||
{
| |||
//--- return result
| |||
return (numerator / denominator);
| |||
}
| |||
else
| |||
{
| |||
//--- return NAN
| |||
return (MathLog(-1));
| |||
}
| |||
}
| |||
else
| |||
if(m_defuzz_method==BisectorDef)
| |||
{
| |||
//+-------------------------------------------------------------------------------------+
| |||
//| The method Bisector consists in finding the point on the abscissa, |
| |||
//| which divides the area under the curve of the membership function in two equal parts|
| |||
//+-------------------------------------------------------------------------------------+
| |||
double Area=0.0; // The area under the function
| |||
int k=50; // The function is divided into "k" steps
| |||
double now=min; // The current position
| |||
for(int i=0; i<k; i++)
| |||
{
| |||
Area+=mf.GetValue(now);
| |||
now=now+(max-min)/k;
| |||
}
| |||
now=min;
| |||
double halfArea=fabs(Area/2-mf.GetValue(min));
| |||
Area=0.0;
| |||
while(true)
| |||
{
| |||
Area+=mf.GetValue(now);
| |||
if(Area>=halfArea)
| |||
{
| |||
break;
| |||
}
| |||
now=now+(max-min)/k;
| |||
}
| |||
delete mf;
| |||
//--- return result
| |||
return (now);
| |||
}
| |||
else
| |||
if(m_defuzz_method==AverageMaximumDef)
| |||
{
| |||
//+------------------------------------------------------------------------------------------+
| |||
//| AverageMaximum method is the arithmetic mean of all the maxima of the membership function|
| |||
//+------------------------------------------------------------------------------------------+
| |||
double sum_max=0; // Sum of local maxima
| |||
double count_max=0; // Count of local maxima
| |||
int k=50; // The function is divided into "k" steps
| |||
double now=min; // The current position
| |||
double step=(max-min)/k; // Calculate the step function
| |||
for(int i=1; i<k; i++)
| |||
{
| |||
double point_1 = mf.GetValue(now);
| |||
double point_0 = mf.GetValue(now - step);
| |||
double point_2 = mf.GetValue(now + step);
| |||
//--- check the first element
| |||
if(i==1)
| |||
{
| |||
if(mf.GetValue(min)>mf.GetValue(min+step))
| |||
{
| |||
sum_max+=mf.GetValue(min);
| |||
count_max++;
| |||
}
| |||
}
| |||
//--- check the second element
| |||
if(i==k-1)
| |||
{
| |||
if(mf.GetValue(max)>mf.GetValue(max-step))
| |||
{
| |||
sum_max+=mf.GetValue(max);
| |||
count_max++;
| |||
}
| |||
}
| |||
//--- check all the other elements
| |||
if((point_1>point_0) && (point_1>point_2))
| |||
{
| |||
sum_max+=point_1;
| |||
count_max++;
| |||
}
| |||
}
| |||
if(count_max==0)
| |||
{
| |||
delete mf;
| |||
//--- return result
| |||
return (0);
| |||
}
| |||
else
| |||
{
| |||
delete mf;
| |||
//--- return result
| |||
return (sum_max/count_max);
| |||
}
| |||
}
| |||
else
| |||
if(m_defuzz_method==LargestMaximumDef)
| |||
{
| |||
CArrayDouble *local_max=new CArrayDouble; // Array of all local maximum
| |||
double result; // Result of defuzzification method
| |||
int k=50; // The function is divided into "k" steps
| |||
double now=min; // The current position
| |||
double step=(max-min)/k; // Calculate the step function
| |||
for(int i=1; i<k; i++)
| |||
{
| |||
double point_1 = mf.GetValue(now);
| |||
double point_0 = mf.GetValue(now - step);
| |||
double point_2 = mf.GetValue(now + step);
| |||
//--- check the first element
| |||
if(i==1)
| |||
{
| |||
if(mf.GetValue(min)>mf.GetValue(min+step))
| |||
{
| |||
local_max.Add(mf.GetValue(min));
| |||
}
| |||
}
| |||
//--- check the second element
| |||
if(i==k-1)
| |||
{
| |||
if(mf.GetValue(max)>mf.GetValue(max-step))
| |||
{
| |||
local_max.Add(mf.GetValue(max));
| |||
}
| |||
}
| |||
//--- check all the other elements
| |||
if((point_1>point_0) && (point_1>point_2))
| |||
{
| |||
local_max.Add(point_1);
| |||
}
| |||
now+=step;
| |||
}
| |||
result=local_max.At(0);
| |||
for(int i=0; i<local_max.Total(); i++)
| |||
{
| |||
if(result<=local_max.At(i))
| |||
{
| |||
result=local_max.At(i);
| |||
}
| |||
}
| |||
now=min;
| |||
while(true)
| |||
{
| |||
if(mf.GetValue(now)==result)
| |||
{
| |||
break;
| |||
}
| |||
now+=step;
| |||
}
| |||
delete local_max;
| |||
delete mf;
| |||
//--- return result
| |||
return (now);
| |||
}
| |||
else
| |||
if(m_defuzz_method==SmallestMaximumDef)
| |||
{
| |||
CArrayDouble *local_max=new CArrayDouble; // Array of all local maximum
| |||
double result; // Result of defuzzification method
| |||
int k=50; // The function is divided into "k" steps
| |||
double now=min; // The current position
| |||
double step=(max-min)/k; // Calculate the step function
| |||
for(int i=1; i<k; i++)
| |||
{
| |||
double point_1 = mf.GetValue(now);
| |||
double point_0 = mf.GetValue(now - step);
| |||
double point_2 = mf.GetValue(now + step);
| |||
//--- check the first element
| |||
if(i==1)
| |||
{
| |||
if(mf.GetValue(min)>mf.GetValue(min+step))
| |||
{
| |||
local_max.Add(mf.GetValue(min));
| |||
}
| |||
}
| |||
//--- check the second element
| |||
if(i==k-1)
| |||
{
| |||
if(mf.GetValue(max)>mf.GetValue(max-step))
| |||
{
| |||
local_max.Add(mf.GetValue(max));
| |||
}
| |||
}
| |||
//--- check all the other elements
| |||
if((point_1>point_0) && (point_1>point_2))
| |||
{
| |||
local_max.Add(point_1);
| |||
}
| |||
now+=step;
| |||
}
| |||
result=local_max.At(0);
| |||
for(int i=0; i<local_max.Total(); i++)
| |||
{
| |||
if(result>=local_max.At(i))
| |||
{
| |||
result=local_max.At(i);
| |||
}
| |||
}
| |||
now=min;
| |||
while(true)
| |||
{
| |||
if(mf.GetValue(now)==result)
| |||
{
| |||
break;
| |||
}
| |||
now+=step;
| |||
}
| |||
delete local_max;
| |||
delete mf;
| |||
//--- return result
| |||
return (now);
| |||
}
| |||
else
| |||
{
| |||
Print("Internal exception.");
| |||
delete mf;
| |||
//--- return
| |||
return (0);
| |||
}
| |||
}
| |||
//+------------------------------------------------------------------+
|