ファジイ推論

  以下,複数のファイル構成になっています.ファイル間の区切りを「---・・・」で示します.makefile の部分は,make コマンドを使用してコンパイルする場合に必要となる makefile の例です.make を使用しない場合は必要ありません.

------------------------makefile------------------
#
#     リンク
#
CFLAGS = -c -Wall -O2
OBJECT = fz.o cons.o dest.o Cross.o Height.o Inf.o Result.o
pgm: $(OBJECT)
	g++ $(OBJECT) -o fz -lm
#
#     コンパイル
#
fz.o: fuzzy.h fz.cpp
	g++ $(CFLAGS) fz.cpp
cons.o: fuzzy.h cons.cpp
	g++ $(CFLAGS) cons.cpp
dest.o: fuzzy.h dest.cpp
	g++ $(CFLAGS) dest.cpp
Cross.o: fuzzy.h Cross.cpp
	g++ $(CFLAGS) Cross.cpp
Height.o: fuzzy.h Height.cpp
	g++ $(CFLAGS) Height.cpp
Inf.o: fuzzy.h Inf.cpp
	g++ $(CFLAGS) Inf.cpp
Result.o: fuzzy.h Result.cpp
	g++ $(CFLAGS) Result.cpp

------------------------規則ファイル--------------
推論方法 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

------------------------fuzzy.h-------------------
/*********************/
/* クラスFuzzyの定義 */
/*********************/
class Fuzzy {
		double ***left;   // [i][j][0] : 中心
                          //       [1] : 幅
		double **right;   // [i][0] : 中心
                          //    [1] : 幅
		double *omega;   // ωの値
		int bun;   // 積分の分割数
		int method;   // 推論方法
                      //    =0 : and,or
                      //    =1 : *,or
                      //    =2 : *,+
		int n_rule;   // 規則の数
		int n_val;   // 変数の数
		int **rule;   // [i][j] =0 : i番目の規則はj番目の変数を使用しない
                      //        =1 : i番目の規則はj番目の変数を使用する
	public:
		Fuzzy(char *);   // コンストラクタ
		~Fuzzy();   // デストラクタ
		double Cross(double, double, double, double);
		double Height(double, double *);
		double Inf(double *);   // 推論の実行
		double Result(double *);
};

------------------------constructor---------------
/************************************/
/* コンストラクタ                   */
/*      name : 入力データファイル名 */
/************************************/
#include <stdio.h>
#include "fuzzy.h"

Fuzzy::Fuzzy(char *name)
{
	int i1, i2;
	FILE *in;

	in = fopen(name, "r");
/*
     基本データの入力
*/
	fscanf(in, "%*s %d %*s %d %*s %d %*s %d",
           &method, &n_rule, &n_val, &bun);
/*
     領域の確保
*/
	left  = new double ** [n_rule];
	right = new double * [n_rule];
	omega = new double [n_rule];
	rule  = new int * [n_rule];

	for (i1 = 0; i1 < n_rule; i1++) {
		left[i1]  = new double * [n_val];
		right[i1] = new double [2];
		rule[i1]  = new int [n_val];
		for (i2 = 0; i2 < n_val; i2++)
			left[i1][i2]  = new double [2];
	}
/*
     規則データの入力
*/
	for (i1 = 0; i1 < n_rule; i1++) {

		fscanf(in, "%*s");
						// 左辺
		for (i2 = 0; i2 < n_val; i2++) {
			fscanf(in, "%*s %d", &(rule[i1][i2]));
			if (rule[i1][i2] > 0)
				fscanf(in, "%*s %lf %lf", &(left[i1][i2][0]), &(left[i1][i2][1]));
		}
						// 右辺
		fscanf(in, "%*s %lf %lf", &(right[i1][0]), &(right[i1][1]));
	}

	fclose(in);
}

------------------------destructor----------------
/****************/
/* デストラクタ */
/****************/
#include "fuzzy.h"

Fuzzy::~Fuzzy()
{
	int i1, i2;

	for (i1 = 0; i1 < n_rule; i1++) {
		for (i2 = 0; i2 < n_val; i2++)
			delete [] left[i1][i2];
		delete [] left[i1];
		delete [] right[i1];
		delete [] rule[i1];
	}

	delete [] left;
	delete [] right;
	delete [] rule;
}

------------------------Cross.cpp----------------
/*******************************************/
/* 交点の計算                              */
/*      x : x                              */
/*      c : 中心                           */
/*      h : 幅                             */
/*      r : <0 : 左辺のメンバーシップ関数  */
/*          >=0 : 右辺(ω)                 */
/*      return : 交点                      */
/*******************************************/
#include "fuzzy.h"

double Fuzzy::Cross(double x, double c, double h, double r)
{
	double x1, x2, y, y1, y2;

	if (x < c) {
		x1 = c - h;
		x2 = c;
		y1 = 0.0;
		y2 = (r < 0.0 || (r >= 0.0 && method == 0)) ? 1.0 : r;
	}
	else {
		x1 = c;
		x2 = c + h;
		y1 = (r < 0.0 || (r >= 0.0 && 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 && method == 0 && y > r)
			y = r;
	}

	return y;
}

------------------------Height.cpp----------------
/**********************************************/
/* 右辺の計算                                 */
/*      x : 値                                */
/*      om[i] : <0 : i番目の規則を使用しない */
/*              >=0 : ωの値                  */
/*      return : xにおける右辺の値           */
/**********************************************/
#include "fuzzy.h"

double Fuzzy::Height(double x, double *om)
{
	double y = 0.0, y1;
	int i1, sw;

	sw = 0;

	for (i1 = 0; i1 < n_rule; i1++) {

		if (om[i1] >= 0.0) {

			y1 = Cross(x, right[i1][0], right[i1][1], om[i1]);

			if (sw == 0) {
				y  = y1;
				sw = 1;
			}

			else {
				if (method < 2) {
					if (y1 > y)
						y = y1;
				}
				else
					y += y1;
			}
		}
	}

	return y;
}

------------------------Inf.cpp-------------------
/**************************/
/* 推論の実行             */
/*      x[i] : 各変数の値 */
/*      return : 推論値   */
/**************************/
#include "fuzzy.h"

double Fuzzy::Inf(double *x)
{

	double x1, y;
	int i1, i2;
/*
     ωの計算
*/
	for (i1 = 0; i1 < n_rule; i1++) {

		omega[i1] = -1.0;

		for (i2 = 0; i2 < n_val; i2++) {

			if (rule[i1][i2] > 0) {

				if (x[i2] > left[i1][i2][0]-left[i1][i2][1] &&
                    x[i2] < left[i1][i2][0]+left[i1][i2][1])
					x1 = Cross(x[i2], left[i1][i2][0], left[i1][i2][1], -1.0);
				else
					x1 = 0.0;

				if (omega[i1] < 0.0)
					omega[i1] = x1;
				else {
					if (method == 0) {
						if (x1 < omega[i1])
							omega[i1] = x1;
					}
					else
						omega[i1] *= x1;
				}
			}
		}
	}
/*
     右辺の計算
*/
	y = Result(omega);

	return y;
}

------------------------Result.cpp----------------
/**********************************************/
/* 右辺による推論                             */
/*      om[i] : <0 : i番目の規則を使用しない */
/*              >=0 : ωの値                  */
/*      return : 推論値                       */
/**********************************************/
#include <stdio.h>
#include <math.h>
#include "fuzzy.h"

double Fuzzy::Result(double *om)
{
	double h, max = 0.0, min = 0.0, x1, x2, y = 0.0, y1, y2, z1, z2;
	int i1, sw;
/*
     積分範囲と積分幅の計算
*/
	sw = 0;

	for (i1 = 0; i1 < n_rule; i1++) {
		if (om[i1] >= 0.0) {
			x1 = right[i1][0] - right[i1][1];
			x2 = right[i1][0] + 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) / bun;

	if (h < 1.0e-15) {
		printf("***error  invalid data (h = 0) ***\n");
		exit(1);
	}
/*
     積分(重心の計算,台形則)
*/
	z1 = Height(min, om);
	z2 = Height(max, om);
	y1 = 0.5 * (min * z1 + max * z2);
	y2 = 0.5 * (z1 + z2);
	x1 = min;

	for (i1 = 0; i1 < bun-1; i1++) {
		x1 += h;
		z1  = Height(x1, om);
		y1 += x1 * z1;
		y2 += z1;
	}

	y1 *= h;
	y2 *= h;

	if (fabs(y2) > 1.0e-10)
		y = y1 / y2;
	else {
		printf("***error  invalid data (y2 = 0) ***\n");
		exit(1);
	}

	return y;
}

------------------------main----------------------
/****************************/
/* ファジイ推論             */
/*      coded by Y.Suganuma */
/****************************/
#include <stdio.h>
#include <stdlib.h>
#include "fuzzy.h"

/****************/
/* main program */
/****************/
int main(int argc, char *argv[])
{
	double *x, **xx, y;
	int i1, i2, n_d, n_val;
	FILE *in, *out;

	if (argc == 4) {
					// オブジェクトの生成
		Fuzzy fz(argv[1]);
					// 推論データの読み込み
		in = fopen(argv[2], "r");
		fscanf(in, "%*s %d %*s %d", &n_val, &n_d);
		x  = new double [n_val];
		xx = new double * [n_val];
		for (i1 = 0; i1 < n_val; i1++) {
			fscanf(in, "%*s");
			xx[i1] = new double [n_d];
			for (i2 = 0; i2 < n_d; i2++)
				fscanf(in, "%lf", &xx[i1][i2]);
		}
		fclose(in);
					// 推論とその結果の出力
		out = fopen(argv[3], "w");
		for (i1 = 0; i1 < n_d; i1++) {
			for (i2 = 0; i2 < n_val; i2++) {
				x[i2] = xx[i2][i1];
				fprintf(out, "%f ", x[i2]);
			}
			y = fz.Inf(x);   // 推論
			fprintf(out, "%f\n", y);
		}
		fclose(out);
	}

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

	return 0;
}
		

  コンパイルした後,

実行可能ファイル名 規則ファイル名 推論ファイル名 出力ファイル名

と入力してやれば実行できます.出力ファイル名は,結果を出力するファイルの名前です.また,規則ファイル名は,規則等を記述したファイルの名前であり,たとえば以下のような形式で作成します.
推論方法 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
  日本語で記述した部分(「推論方法」,「規則の数」等)は,次に続くデータの説明ですのでどのように修正しても構いませんが,削除したり,または,複数の文(間に半角のスペースを入れる)にするようなことはしないでください.上のデータは,次に示す 3 つの規則を表現したものであり,また,各データの意味はそれ以降に示す通りです.

   if xがおよそ1 then yはおよそ1
   if xがおよそ2 then yはおよそ11
   if xがおよそ3 then yはおよそ21

推論方法

=0 : AND, OR  AND 演算と OR 演算を利用
=1 : *, OR  AND 演算の代わりに乗算を利用
=2 : *, +  上に加え,OR 演算の代わりに加算を利用

規則の数

  規則の数を入力します

変数の数

  変数の数を入力します

積分の分割数

  推論値を求めるため,メンバーシップ関数の数値積分を行います.その際の分割数を入力します.

規則1

  次の「規則2」までのデータを規則の数だけ繰り返して入力します.

変数1 1 中心と幅 1.0 1.0

  この例では,変数の数が 1 であるため,1 行だけですが,一般的には,この行のデータを変数の数だけ入力します.「変数1」の次の数字は,この規則で対応する変数を使用するか否かを指定するためのものです.0 の場合は使用しない(「中心と幅」以降のデータは入力しないでください),また,1 の場合は使用することを意味しています.使用する場合は,メンバーシップ関数の形を決めるため,「中心と幅」以降のデータが必要となります.このプログラムでは,三角型のメンバーシップ関数だけを扱っています.「中心と幅」に続く 2 つのデータは,三角形の中心の位置と,底辺の半分の長さを意味しています.

右辺(中心と幅) 1.0 15.0

  規則の右辺のメンバーシップ関数の形を指定します.右辺におけるメンバーシップ関数の形も三角形です.

  推論ファイルは,推論値を得たい x の値を指定するためのファイルであり,以下のような形式で作成します.
変数の数 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
  各データの意味は以下の通りです.

変数の数

  変数の数を入力します.規則ファイルで指定した値と一致する必要があります.

データ数

  各変数に与えるデータ数を入力します.

変数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

  推論結果を得たいデータを入力します.1 つの変数に対するデータが複数行になっても構いません.変数の数だけこのデータが必要になります.

  以上のデータを準備して実行すると,推論データに対応した推論結果がファイルに出力されます.