Arquivos
fuzzy_logic/demo/fuzzy-logic-x-ajax-tank.php
2013-01-23 20:18:12 -05:00

699 linhas
19 KiB
PHP

<?php
error_reporting(5);
define ("LINFINITY" , -1 );
define ("TRIANGLE" , 0 );
define ("RINFINITY" , 1 );
define ("TRAPEZOID" , 2 );
/*---- required additional function ------------*/
function getValve($Fuzzy,$Valve,$inpMax,$lb,$ub) {
$xdot = $Valve+$Fuzzy;
if ($xdot<=$lb) $Valve = $lb;
elseif ($xdot>=$ub) $Valve = $ub;
else $Valve=min(max($xdot,$lb),$ub);
return array($Valve,$Valve * $inpMax);
}
function getLastErrors($demandLevel,$currentLevel,$lastLevel) {
$erate = $currentLevel-$lastLevel;
if ($erate < -0.1) $erate =-0.1;
if ($erate > 0.1) $erate = 0.1;
$elevel = $demandLevel-$currentLevel ;
return array($erate,$elevel);
}
class Member {
protected
$FName, // Member Name
$FMiddle, // Middle Member point
$FA, // Start Member point
$FB, // End member point
$FType; // Member type TRIANGLE,LINFINITY,RFINFINITY, TRAPEZOID
public function __construct($Name=NULL,$start=NULL,$medium=NULL,$stop=NULL,$type=NULL){
if($Name == NULL) return;
$this->FName = $Name;
$this->FMiddle = $medium;
$this->FA = $start;
$this->FB = $stop;
$this->FType = $type;
}
public function __toString(){
return "Member\tname: $this->FName,
middle: $this->FMiddle,
start : $this->FA,
fb : $this->FB,
type : $this->FType";
}
/**
* Calculates the ratio of belonging to a set of defined function shape.
* $ratio = Fuzzification($pontX);
* @param float $poinX
* return float
*/
public function Fuzzification($P=0.0) {
if (($P<$this->FA) OR ($P>$this->FB)) return 0; //P is out this segment...
if ($P==$this->FMiddle) return 1;
if ($this->FType==LINFINITY) {
if ($P<=$this->FMiddle) return 1;
if (($P>$this->FMiddle) AND ($P<$this->FB)) return ($this->FB-$P)/($this->FB-$this->FMiddle);
}
if ($this->FType==RINFINITY) {
if ($P>=$this->FMiddle) return 1;
if (($P<$this->FMiddle) AND ($P>$this->FA)) return ($P-$this->FA)/($this->FMiddle-$this->FA);
}
if ($this->FType==TRIANGLE) {
if(($P<$this->FMiddle) AND ($P>$this->FA)) return ($P-$this->FA)/($this->FMiddle-$this->FA);
if(($P>$this->FMiddle) AND ($P<$this->FB)) return ($this->FB-$P)/($this->FB-$this->FMiddle);
}
if ($this->FType==TRAPEZOID) {
if(($P<$this->FMiddle[0]) AND ($P>$this->FA)) return ($P-$this->FA)/($this->FMiddle[0]-$this->FA);
if(($P>$this->FMiddle[1]) AND ($P<$this->FB)) return ($this->FB-$P)/($this->FB-$this->FMiddle[1]);
if (($P>=$this->FMiddle) and ($P<=$this->FMiddle)) return 1;
}
return 0;
}
} // class Member
class Fuzzify extends Member {
protected $FMin = array();
protected $FMax = array();
protected $members = array();
public function setMinMax($idx,$A=0,$B=0) {
If ($A<=$B) {
$this->FMin[$idx] = $A;
$this->FMax[$idx] = $B;
} else {
$this->FMin[$idx] = $B;
$this->FMax[$idx] = $A;
}
}
public function clearMembers() {
$this->members = NULL;
}
public function addMember($idx,$Name='New',$start=0.0,$medium=0.0,$stop=0.0,$type=TRIANGLE) {
$member = new Member($Name,$start,$medium,$stop,$type);
if ($member->FA < (float)$this->FMin[$idx]) $this->setMinMax($idx, $member->FA ,(float)$this->FMax[$idx]);
if ($member->FB > (float)$this->FMax[$idx]) $this->setMinMax($idx , (float)$this->FMin[$idx] , $member->FB);
$this->members[$idx][] = $member;
}
public function setMembers($idx,$m = array()) {
$this->members[$idx] = $m;
}
public function getMembers($idx,$id = NULL) {
if ($id) return $this->members[$idx][$id];
else return $this->members[$idx];
}
public function getMembersIndex($idx,$value = NULL) {
foreach ($this->members[$idx] as $idx => $member)
if ($value==$member->FName) return $idx;
return FALSE;
}
public function getMemberByName($idx,$name = NULL) {
foreach ($this->members[$idx] as $idx => $member)
if ($name==$member->FName) return $member;
return FALSE;
}
} // end class fuzzify
class Rules extends Fuzzify{
public $FXValues = array();
public $FYValues = array();
public $FRealInput = array();
public $FOutputs = array();
private $InputNames = NULL;
private $OutputNames = NULL;
private $rules = NULL;
/**
* Set private properties $this->InputNames
* example :
* $fuzzy->setInputNames(array('Name1','Name2'));
* @param array of string
* return none
**/
public function setInputNames($val) {
$this->InputNames = $val;
}
/**
* Set private properties $this->OutputNames
* example :
* $fuzzy->setOutputNames(array('Name1','Name2'));
* @param array of string
* return none
**/
public function setOutputNames($val) {
$this->OutputNames = $val;
}
/**
* Return private properties $this->InputNames
* example :
* $names = $fuzzy->getInputNames();
* @param none
* return array of string
**/
public function getInputNames() {
return $this->InputNames;
}
/**
* Return private properties $this->OutputNames
* example :
* $names = $fuzzy->getOututNames();
* @param none
* return array of string
**/
public function getOutputNames() {
return $this->OutputNames;
}
/**
* Return private properties $this->InputNames[$idx]
* example :
* $names = $fuzzy->getInputNames($idx);
* @param none
* return string
**/
public function getInputName($idx) {
return $this->InputNames[$idx];
}
/**
* Return private properties $this->OutputNames[$idx]
* example :
* $names = $fuzzy->getOutputNames($idx);
* @param none
* return string
**/
public function getOutputName($idx=0) {
return $this->OutputNames[$idx];
}
/**
* Clear all rules
* example :
* $fuzzy->clearRules();
* @param none
* return none
**/
public function clearRules() {
$this->rules = NULL;
}
/**
* Return array as Rules
* example :
* $fuzzy->getRules();
* @param none
* return array of string
**/
public function getRules() {
return $this->rules;
}
/**
* Return Rule as String
* example :
* $fuzzy->getRule($id);
* @param integer $id (key)
* return string
**/
public function getRule($id) {
return $this->rules[$id];
}
/**
* Add Rule as String to private properties Rules Array
* example :
* $fuzzy->addRule('IF input1.High AND input2.Slow Then Out1.Run');
* @param string
* return none
**/
public function addRule($val) {
$this->rules[] = $val;
}
/**
* Find parenthis fragments of Rule as string
* example :
* $fragment = $this->rSplit('IF input1.High AND (input2.Slow OR input3.Fast) Then Out1.Run');
* @param string
* return string 'input2.Slow OR input3.Fast'
**/
private function rSplit($string) {
if (preg_match("/\((([^()]*|(?R))*)\)/",$string,$matches))
return $matches[1];
}
/**
* Get last nested parenthis fragments of Rule as string
* example :
* $fragment = $this->getLastParent('IF input1.High AND (input2.Slow OR (input3.Fast AND input4.Warm)) Then Out1.Run');
* @param string
* return string 'input3.Fast AND input4.Warm'
**/
private function getLastParent($a) {
do {
$a = $this->rSplit($a);
if ($a) $ret=$a;
} while($a);
return $ret;
}
/**
* Fuzzy Logic OR operation on array of values
* example :
* $val = $this->_FuzzyOR(array(1,0.5));
* @param array float values
* return float (max value) = 0.5
**/
private function _FuzzyOR($arr) {
return (max($arr));
}
/**
* Fuzzy Logic OR operation on array of values
* example :
* $val = $this->rSplit(array(1,0.5));
* @param array float values
* return float (min value) = 1
**/
private function _FuzzyAND($arr) {
return (min($arr));
}
/**
* Calculate Rule.Parser And Interpreter for Rule.
* Rule has text line example:
* IF input1.High AND input2.Slow Then Out1.Run
* Where
* Operator : IF
* Argument Input 1 : input1.High
* where InputName = input1
* DotSeparator = .
* MemberName = Run
* Operator : AND Operation for Arg1 , Arg 2 values
* Argument Input 2 : input2.Slow
* where InputName = input2
* DotSeparator = .
* MemberName = Slow
* Operator : Then
* Argument Output 1 : Out1.Run (InputName,DotSeparator,MemberName)
*
*
* example :
* $val = $fuzzy->processRule('IF input1.High AND input2.Slow Then Out1.Run');
* @param string Rule
* return float (calculated fuzzy value as Rule cryteria)
**/
public function processRule($rule) {
while ($in_parent = $this->getLastParent($rule)) {
$pos=strpos($rule,$in_parent);
$len=strlen($in_parent);
$tmparr=array();
$items=preg_split("/\s+/",$in_parent);
$operation='and';
foreach($items as $item) {
$inp=strtolower($item);
if (($inp=='or') or ($inp=='and')) $operation=$inp; else {
list($inputName,$memberName) = preg_split("/\./",$item);
// get value from
$mem_idx = $this->getMembersIndex($inputName,$memberName);
$tmparr[] =$this->FOutputs[$inputName][$mem_idx];
}
}
$value1 = ($operation == 'or') ? $this->_FuzzyOR($tmparr) : $this->_FuzzyAND($tmparr);
$rule=substr($rule,0,$pos-1).$value1.substr($rule,$pos+$len+1);
}
$items=preg_split("/\s+/",$rule);
$operation='and';
$firstop = array_shift($items);
$outitem = array_pop($items);
$tmparr=array();
foreach($items as $item) {
$inp=strtolower($item);
if (($inp=='or') or ($inp=='and')) $operation=$inp;
elseif (($inp=='then') or ($inp=='for')) continue;
else {
// split names
list($inputName,$memberName) = preg_split("/\./",$item);
// get value from FOutputs
$mem_idx = $this->getMembersIndex($inputName,$memberName);
$tmparr[] =$this->FOutputs[$inputName][$mem_idx];
}
}
$value1 = ($operation == 'or') ? $this->_FuzzyOR($tmparr) : $this->_FuzzyAND($tmparr);
return array($outitem,$value1);
}
} //class Rules
class Fuzzy_Logic extends Rules {
protected $FuzzyTable = NULL;
public $StateOutput = array();
protected $AgregatePoints = 100;
/*
* Clear all solutions arrays in class and next calculations is Ready
* example :
* $this->ClearSolution();
* @param none
* return none
*/
private function ClearSolution() {
$this->FXValues = array();
$this->FYValues = array();
}
/*
* Set protected properties FuzzyTable
* example :
* $x->setFuzzyTable(array(
* // IF input1 AND input2 Then Output
* // For OR use pair input1 , NULL
* // NULL , input 2
* // ------ ------- -------
* array('adequate', NULL , 'low'),
* array(NULL , 'small' , 'low'),
* array('marginal', 'large' , 'normal'),
* array('inadequate' , NULL , 'high'),
* ));
* @param array
* return none
*/
public function SetFuzzyTable($A = array()) {
$this->FuzzyTable = $A;
}
/*
* Set Real Input Value =$X for Input named $idx
* example :
* $fuzzy->SetRealInput('input1',0.23);
* @param string $idx
* @param float $X
* return none
*/
public function SetRealInput($idx,$X = 0.0) {
$this->FRealInput[$idx] = $X;
$this->FOutputs[$idx] = array();
For ($i=0;$i<count($this->members[$idx]);$i++) {
$this->FOutputs[$idx][] = $this->members[$idx][$i]->Fuzzification($this->FRealInput[$idx]);
}
}
/*
* Agregate All Rules Result for Defuzification
* example :
* $fuzzy->FuzzyAgregate($outname,$Member,$AlphaCut=0.0)
* @param string $output_name
* @param object $member_object
* @param float $calculate_rule_value
* return none
*/
public function FuzzyAgregate($outname,$Member,$AlphaCut=0.0) {
foreach($this->FXValues[$outname] as $index=>$pointX) {
if ($pointX<$Member->FA) continue;
if ($pointX>$Member->FB) break;
$ms = $Member->Fuzzification($pointX);
$mem_val = min($ms,$AlphaCut);
$this->FYValues[$outname][$index] = max($this->FYValues[$outname][$index],$mem_val);
}
}
/*
* Calculate Defuzification Fuzzy Result for method AVG (required set FuzzyTable)
* example :
* $fuzzy->calcFuzzy()
* @param none
* return array of outputs values (no associated keys)
*/
public function calcFuzzyAlt() {
$MaxAverage = 0;
$this->ClearSolution();
//$count_inputs = count($this->InputNames);
$sum = 0;
$tmpx=array();
$sum = array();
$cnt = array();
// fill output agregate table
foreach($this->getOutputNames() as $outname) {
$AgregateDeltaX = ($this->FMax[$outname]-$this->FMin[$outname])/$this->AgregatePoints;
$this->FXValues[$outname] = Range($this->FMin[$outname],$this->FMax[$outname],$AgregateDeltaX);
$this->FYValues[$outname] = array_fill ( 0 , count($this->FXValues[$outname]), 0.0 );
}
foreach ($this->FuzzyTable as $row_idx => $line_rule) {
$sum = 0.0;
$cnt = 0.0;
$count_inputs = count($line_rule)-1; // last is output
foreach ($line_rule as $col => $member_name) {
$out_idx =$col - $count_inputs;
$outname = $this->getOutputName($out_idx);
if (!is_null($member_name)) {
if ($col<$count_inputs) { // is input
$inp_name = $this->getInputName($col);
$mem_idx = $this->getMembersIndex($inp_name,$member_name);
$val =$this->FOutputs[$inp_name][$mem_idx]; // get members value
if ($val>0) {
$sum+=$val; // sum members values
$cnt++;
} else {
//$sum=0;
//$cnt=0;
break;
}
} else { //is output
$member=$this->getMemberByName($outname,$member_name); // get OUTPUT member
if ($cnt == 0) $avg_sum = 0; else $avg_sum = $sum/$cnt;
$this->StateOutput[$member_name] = $avg_sum;
if ($avg_sum>0) $this->FuzzyAgregate($outname,$member,$avg_sum);
$sum = 0.0;
$cnt = 0.0;
}
} // if $member_name
} // foreach rule
} // foreach rule_row
$result = array();
foreach($this->getOutputNames() as $outname) {
$suma=0.0;
$sumb=0.0;
foreach($this->FXValues[$outname] as $id=>$x) {
$y=$this->FYValues[$outname][$id];
if ($y>0) {
$suma+=($x*$y);
$sumb+=$y;
}
}
if ($sumb == 0) $result[]= 0; else $result[] = $suma/$sumb;
}
return $result;
}
/*
* Calculate Defuzification Fuzzy Result for method MIN,MAX (required set Rules)
* example :
* $fuzzy->calcFuzzyAlt()
* @param none
* return array of outputs values (associated keys)
*/
public function calcFuzzy() {
$this->ClearSolution();
$sum = 0;
$tmpx=array();
$sum = array();
$cnt = array();
// fill output agregate table
foreach($this->getOutputNames() as $outname) {
$AgregateDeltaX = ($this->FMax[$outname]-$this->FMin[$outname])/$this->AgregatePoints;
$this->FXValues[$outname] = Range($this->FMin[$outname],$this->FMax[$outname],$AgregateDeltaX);
$this->FYValues[$outname] = array_fill ( 0 , count($this->FXValues[$outname]), 0.0 );
}
$rules=$this->getRules();
foreach ($rules as $key=>$rule) {
list($outItem,$value) = $this->processRule($rule);
list($outputName,$memberName) = preg_split("/\./",$outItem);
$this->StateOutput[$memberName] = $value;
$member=$this->getMemberByName($outputName,$memberName); // get OUTPUT member
if ($value>0) $this->FuzzyAgregate($outputName,$member,$value);
}
$result = array();
foreach($this->getOutputNames() as $outname) {
$suma=0.0;
$sumb=0.0;
foreach($this->FXValues[$outname] as $id=>$x) {
$y=$this->FYValues[$outname][$id];
if ($y>0) {
$suma+=($x*$y);
$sumb+=$y;
}
}
if ($sumb == 0) $result[$outname]= 0; else $result[$outname] = $suma/$sumb;
}
return $result;
}
} //class
/*--------------------------- start tank demo controll --------*/
$x = new Fuzzy_Logic();
$x->clearMembers();
/* ---------- set input members ---------*/
$x->setInputNames(array('ERROR','RATE'));
$x->addMember($x->getInputName(0),'E_NEG',-1 ,-0.9 , 0 ,LINFINITY);
$x->addMember($x->getInputName(0),'E_OK' ,-0.1 , 0 , 0.1,TRIANGLE);
$x->addMember($x->getInputName(0),'E_POS', 0 , 0.9 , 1 ,RINFINITY);
$x->addMember($x->getInputName(1),'R_NEG',-0.10,-0.07 ,0 ,LINFINITY);
$x->addMember($x->getInputName(1),'R_OK', -0.07, 0 ,0.07 ,TRIANGLE);
$x->addMember($x->getInputName(1),'R_POS', 0 , 0.07 ,0.1 ,RINFINITY);
/* ---------- set output members ---------*/
$x->setOutputNames(array('OUT'));
$x->addMember($x->getOutputName(0),'CF',-1.0, -0.9 ,-0.8 ,TRIANGLE);
$x->addMember($x->getOutputName(0),'CS',-0.6, -0.5 ,-0.4 ,TRIANGLE);
$x->addMember($x->getOutputName(0),'NC',-0.1, 0.0 , 0.1 ,TRIANGLE);
$x->addMember($x->getOutputName(0),'OS', 0.4, 0.5 , 0.6 ,TRIANGLE);
$x->addMember($x->getOutputName(0),'OF', 0.8, 0.9 , 1.0 ,TRIANGLE);
/* ---------- set rules table ------------ */
$x->clearRules();
$x->addRule('IF ERROR.E_NEG THEN OUT.CF');
$x->addRule('IF ERROR.E_OK THEN OUT.NC');
$x->addRule('IF ERROR.E_POS THEN OUT.OF');
$x->addRule('IF ERROR.E_OK AND RATE.R_POS THEN OUT.CS');
$x->addRule('IF ERROR.E_OK AND RATE.R_NEG THEN OUT.OS');
/*------------ Remote parameters----------------*/
$get = $_GET['table'];
$tankLevel = $get["currTanksLevel"];
$tankLastLevel = $get["lastTanksLevel"];
$tankValve = $get["valveTanksInput"];
$set = $_GET['setmode'];
$InputMaxValve = $set["mainInput"];
$OutputXMaxValve = $set["mainOutputX"];
$OutputYMaxValve = $set["mainOutputY"];
$tankDemandLevel = $set["demandTanksLevel"];
$tankInpMax = $set["remoteTanksInput"];
$tankOutMax = $set["remoteTanksOutput"];
$tankMaxWorkLevel = $set["tankMaxWorkLevel"];
$tankMinWorkLevel = $set["tankMinWorkLevel"];
/*------------- Valve parameters -------------*/
$ToutX_stream = 0.02 ; // output stream by time period
$ToutY_stream = 0.03 ; // output stream by time period
$Tinp_stream = 0.07 ;
/*-------------- Initialise ------------------*/
$tankName = array('A', 'B', 'C', 'D', 'E', 'X' , 'Y' );
$tankInputRefKey = array(-1 , 0 , 1 , 2 , 3 , 4 , 3 );
$tankOutputRefKey = array(0 , 1 , 2 , 3 , 4 , -1 , 4 );
$tankMaxLevel = array(1.0,1.0,1.0,1.0,1.0,1.0,1.0);
$tankInpDT = array(0.07,0.07,0.07,0.07,0.07,0.07,0.07);
$tankOutDT = array(0.05,0.05,0.05,0.05,0.05,0.05,0.05);
$tankFuzzy = array(0.0,0.0,0.0,0.0,0.0,0.0,0.0);
/* ----------- Fuzzy Controll Loop ----------------*/
for($tank=0;$tank<7;$tank++) {
$name=$tankName[$tank];
list($erate,$elevel) = getLastErrors($tankDemandLevel[$tank],$tankLevel[$tank],$tankLastLevel[$tank]);
$x->SetRealInput('ERROR', $elevel );
$x->SetRealInput('RATE' , $erate );
$fuzzy_arr = $x->calcFuzzy();
$tankFuzzy[$tank] = $fuzzy_arr['OUT'];
$tankLastLevel[$tank] = $tankLevel[$tank];
$key_input_ref = $tankInputRefKey[$tank];
if($key_input_ref>=0)
$Vinp_max = $tankInpMax[$tank]*$tankInpDT[$tank]*(int)(($tankLevel[$tank]<=$tankMaxWorkLevel[$tank]) and ($tankLevel[$key_input_ref]>$tankMinWorkLevel[$key_input_ref]));
else
$Vinp_max = $InputMaxValve*$Tinp_stream *(int)($tankLevel[$tank]<$tankMaxWorkLevel[$tank]);
list($tankValve[$tank],$WaterInput) = getValve($tankFuzzy[$tank],$tankValve[$tank],$Vinp_max,0,1);
$key_output_ref = $tankOutputRefKey[$tank];
if($key_output_ref>=0)
$VOut_max = $tankOutMax[$tank]*$tankOutDT[$tank]*(int)($tankLevel[$key_output_ref]>$tankMinWorkLevel[$key_output_ref]);
else //-1
$VOut_max = $OutputXMaxValve*$ToutX_stream*(int)($tankLevel[$tank]>$tankMinWorkLevel[$tank]);
$tankLevel[$tank] = min($tankMaxLevel[$tank],$tankLevel[$tank] - $VOut_max + $WaterInput);
}
$out =array(
"currTanksLevel" => $tankLevel ,
"lastTanksLevel" => $tankLastLevel ,
"valveTanksInput" => $tankValve );
echo json_encode($out);
?>