ファジイ推論

<?php

/****************************/
/* ファジイ推論             */
/*      coded by Y.Suganuma */
/****************************/

/*********************/
/* クラスFuzzyの定義 */
/*********************/
class Fuzzy {
	private $left;   // [i][j][0] : 中心
                     //       [1] : 幅
	private $right;   // [i][0] : 中心
                      //    [1] : 幅
	private $omega;   // ωの値
	private $bun;   // 積分の分割数
	private $method;   // 推論方法
                       //    =0 : and,or
                       //    =1 : *,or
                       //    =2 : *,+
	private $n_rule;   // 規則の数
	private $n_val;   // 変数の数
	private $rule;   // [i][j] =0 : i番目の規則はj番目の変数を使用しない
                     //        =1 : i番目の規則はj番目の変数を使用する
	
    /************************************/
	/* コンストラクタ                   */
    /*      name : 入力データファイル名 */
	/************************************/
	function Fuzzy($name)
	{
    	$in = fopen($name, "r");
    /*
         基本データの入力
	*/
		fscanf($in, "%*s %d %*s %d %*s %d %*s %d", $this->method, $this->n_rule, $this->n_val, $this->bun);
    /*
	     領域の確保
    */
		$this->left  = array($this->n_rule);
    	$this->right = array($this->n_rule);
		$this->omega = array($this->n_rule);
		$this->rule  = array($this->n_rule);
	
    	for ($i1 = 0; $i1 < $this->n_rule; $i1++) {
    		$this->left[$i1]  = array($this->n_val);
    		$this->right[$i1] = array(2);
			$this->rule[$i1]  = array($this->n_val);
			for ($i2 = 0; $i2 < $this->n_val; $i2++)
				$this->left[$i1][$i2]  = array(2);
    	}
	/*
         規則データの入力
	*/
    	for ($i1 = 0; $i1 < $this->n_rule; $i1++) {
	
			fgets($in);
							// 左辺
    		for ($i2 = 0; $i2 < $this->n_val; $i2++) {
 			 	$str = trim(fgets($in));
			    strtok($str, " ");
   				$this->rule[$i1][$i2] = intval(strtok(" "));
    			if ($this->rule[$i1][$i2] > 0) {
			    	strtok(" ");
					$this->left[$i1][$i2][0] = floatval(strtok(" "));
					$this->left[$i1][$i2][1] = floatval(strtok(" "));
				}
			}
							// 右辺
    		fscanf($in, "%*s %lf %lf", $this->right[$i1][0], $this->right[$i1][1]);
		}
    
		fclose($in);
    }
	
	/*******************************************/
	/* 交点の計算                              */
    /*      x : x                              */
    /*      c : 中心                           */
    /*      h : 幅                             */
	/*      r : <0 : 左辺のメンバーシップ関数  */
	/*          >=0 : 右辺(ω)                 */
	/*      return : 交点                      */
    /*******************************************/
	function Cross($x, $c, $h, $r)
    {
		if ($x < $c) {
    		$x1 = $c - $h;
			$x2 = $c;
			$y1 = 0.0;
			$y2 = ($r < 0.0 || ($r >= 0.0 && $this->method == 0)) ? 1.0 : $r;
    	}
    	else {
    		$x1 = $c;
			$x2 = $c + $h;
			$y1 = ($r < 0.0 || ($r >= 0.0 && $this->method == 0)) ? 1.0 : $r;
			$y2 = 0.0;
    	}
	
    	$y = $y1 + ($y2 - $y1) * ($x - $x1) / ($x2 - $x1);
	
    	if ($y < 0.0)
			$y = 0.0;
		else {
			if ($r >= 0.0 && $this->method == 0 && $y > $r)
    			$y = $r;
    	}
    
		return $y;
	}
	
    /**********************************************/
	/* 右辺の計算                                 */
    /*      x : 値                                */
	/*      om[i] : <0 : i番目の規則を使用しない */
    /*              >=0 : ωの値                  */
	/*      return : xにおける右辺の値           */
	/**********************************************/
	function Height($x, $om)
    {
    	$y  = 0.0;
    	$sw = 0;
	
		for ($i1 = 0; $i1 < $this->n_rule; $i1++) {
	
    		if ($om[$i1] >= 0.0) {
	
    			$y1 = $this->Cross($x, $this->right[$i1][0], $this->right[$i1][1], $om[$i1]);
	
    			if ($sw == 0) {
					$y  = $y1;
					$sw = 1;
				}
    
    			else {
    				if ($this->method < 2) {
						if ($y1 > $y)
							$y = $y1;
					}
    				else
						$y += $y1;
    			}
			}
    	}
	
		return $y;
	}
    
    /**************************/
    /* 推論の実行             */
	/*      x[i] : 各変数の値 */
	/*      return : 推論値   */
	/**************************/
    function Inf($x)
	{
    /*
	     ωの計算
    */
		for ($i1 = 0; $i1 < $this->n_rule; $i1++) {
	
			$this->omega[$i1] = -1.0;
    
    		for ($i2 = 0; $i2 < $this->n_val; $i2++) {
    
				if ($this->rule[$i1][$i2] > 0) {
	
					if ($x[$i2] > $this->left[$i1][$i2][0]-$this->left[$i1][$i2][1] &&
                        $x[$i2] < $this->left[$i1][$i2][0]+$this->left[$i1][$i2][1])
						$x1 = $this->Cross($x[$i2], $this->left[$i1][$i2][0], $this->left[$i1][$i2][1], -1.0);
    				else
						$x1 = 0.0;
    
					if ($this->omega[$i1] < 0.0)
						$this->omega[$i1] = $x1;
					else {
    					if ($this->method == 0) {
    						if ($x1 < $this->omega[$i1])
    							$this->omega[$i1] = $x1;
						}
						else
							$this->omega[$i1] *= $x1;
    				}
				}
    		}
		}
    /*
	     右辺の計算
	*/
		$y = $this->Result($this->omega);
    
    	return $y;
    }
	
	/**********************************************/
	/* 右辺による推論                             */
    /*      om[i] : <0 : i番目の規則を使用しない */
	/*              >=0 : ωの値                  */
    /*      return : 推論値                       */
	/**********************************************/
    function Result($om)
	{
		$max = 0.0;
		$min = 0.0;
    	$y   = 0.0;
    	$sw  = 0;
    /*
	     積分範囲と積分幅の計算
	*/
		for ($i1 = 0; $i1 < $this->n_rule; $i1++) {
    		if ($om[$i1] >= 0.0) {
				$x1 = $this->right[$i1][0] - $this->right[$i1][1];
    			$x2 = $this->right[$i1][0] + $this->right[$i1][1];
				if ($sw == 0) {
    				$min = $x1;
					$max = $x2;
					$sw  = 1;
				}
    			else {
    				if ($x1 < $min)
    					$min = $x1;
					if ($x2 > $max)
						$max = $x2;
				}
    		}
		}
    
		$h = ($max - $min) / $this->bun;
    
		if ($h < 1.0e-15)
			exit("***error  invalid data (h = 0) ***\n");
    /*
         積分(重心の計算,台形則)
	*/
		$z1 = $this->Height($min, $om);
		$z2 = $this->Height($max, $om);
    	$y1 = 0.5 * ($min * $z1 + $max * $z2);
		$y2 = 0.5 * ($z1 + $z2);
    	$x1 = $min;
	
    	for ($i1 = 0; $i1 < $this->bun-1; $i1++) {
			$x1 += $h;
			$z1  = $this->Height($x1, $om);
			$y1 += $x1 * $z1;
    		$y2 += $z1;
    	}
    
		$y1 *= $h;
		$y2 *= $h;
	
    	if (abs($y2) > 1.0e-10)
			$y = $y1 / $y2;
    	else
			exit("***error  invalid data (y2 = 0) ***\n");
	
		return $y;
    }
}
    
/****************/
/* main program */
/****************/

	if (count($argv) == 4) {
					// オブジェクトの生成
		$fz = new Fuzzy($argv[1]);
					// 推論データの読み込み
		$in = fopen($argv[2], "rb");
		fscanf($in, "%*s %d %*s %d", $n_val, $n_d);
		$x  = array($n_val);
		$xx = array($n_val);
		for ($i1 = 0; $i1 < $n_val; $i1++) {
 			$xx[$i1] = array($n_d);
		 	$str = trim(fgets($in));
			strtok($str, " ");
			for ($i2 = 0; $i2 < $n_d; $i2++)
				$xx[$i1][$i2] = floatval(strtok(" "));
		}
		fclose($in);
					// 推論とその結果の出力
		$out = fopen($argv[3], "w");
		for ($i1 = 0; $i1 < $n_d; $i1++) {
			$str = "";
			for ($i2 = 0; $i2 < $n_val; $i2++) {
				$x[$i2] = $xx[$i2][$i1];
				$str = $str.$x[$i2]." ";
			}
			$y = $fz->Inf($x);   // 推論
			$str = $str."res ".$y."\n";
			fwrite($out, $str);
		}
		fclose($out);
	}

	else
		exit("***error   入力データファイル名を指定して下さい\n");

/*
------------------------規則ファイル--------------
推論方法 0 規則の数 3 変数の数 1 積分の分割数 100
規則1
 変数1 1 中心と幅 1.0 1.0
 右辺(中心と幅) 1.0 15.0
規則2
 変数1 1 中心と幅 2.0 1.0
 右辺(中心と幅) 11.0 15.0
規則3
 変数1 1 中心と幅 3.0 1.0
 右辺(中心と幅) 21.0 15.0

------------------------推論ファイル--------------
変数の数 1 データ数 21
変数1 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.0
*/

?>