MobinMQL/Include/Math/Fuzzy/mamdanifuzzysystem.mqh

557 lines
22 KiB
MQL5
Raw Permalink Normal View History

2025-07-22 14:47:41 +03:00
//+------------------------------------------------------------------+
//| 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);
}
}
//+------------------------------------------------------------------+