ファジィ論理で振る舞いに"あいまいさ"を取り入れる

この記事はGoodpatch Advent Calendar 2020 19日目の記事の[逆行投稿](https://wwws.warnerbros.co.jp/tenetmovie/)です。

以前投稿した [今日から始めるCreative Coding環境3選 \- Qiita](https://qiita.com/nsyee/items/69fb4dbf01240138c65c) の記事で [@thi\-ng/umbrella](https://github.com/thi-ng/umbrella/) というTypeScriptのCreative Coding環境を紹介しました。thi.ng はフレームワークというより単独でも組み合わせても使える膨大なパッケージの集合体（developブランチには140個以上）で、現在も活発に開発が進んでいます。そのthi.ngに最近 [@thi.ng/fuzzy](https://github.com/thi-ng/umbrella/tree/develop/packages/fuzzy) というファジィ論理を扱うライブラリがα版として追加されました。家電やロボット制御などに応用されることの多いファジィ論理をCreative Codingのライブラリに取り入れるのは面白いなと思ったので、今回はそちらを簡単に紹介します。

## ファジィ論理 (Fuzzy Logic)とは
ファジィ論理はコンピューターによる推論に「あいまいさ」を導入します。
ルールベースのロジック（いわゆるif文ですね）は一般に真(true)か偽(false)を推論結果として返しますが、ファジイ論理は真理値が0～1の間の値を取り、真偽の2つの値では表せないようなあいまいな推論を扱えます。

また「暑い」や「涼しい」、「苦い」や「甘辛い」など、人間の認知や言葉で変数を表現できることも特徴です。
定量的な入力値を言語学的変数(Linguistic variables, L-vars)にラベル付けして推論規則を考えることができます。

fuzzyはこうした推論を型付きの計算で簡単に取り使えるようなAPIを提供します。

## fuzzyの使い方

thi-ng はモノレポで構成されているのでパッケージ単位でインストールできます。fuzzy と一緒に可視化を行うヘルパーライブラリの fuzzy-viz もセットでインストールしましょう。

```ssh
$ yarn add @thi.ng/fuzzy @thi.ng/fuzzy-viz
```

ファジィ論理の典型的に使用される温度の例:

```typescript
// L-varsの定義
const temp = variable(
  // 値（温度の取りうる値域）
  [-20, 40],
  {
    凍えそう: invSigmoid(0, 2),
    寒い: trapezoid(-1, 2, 16, 20),
    暖かい: trapezoid(15, 20, 30, 34),
    暑い: sigmoid(32, 2),
  }
);
```

具体的な入力値(= 18)をL-varsに適用

```typescript
evaluate(temp, 18);
// {
//   凍えそう: 2.220446049250313e-16,
//   寒い: 0.5,
//   暖かい: 0.6,
//   暑い: 6.914400106935423e-13
// }
```

具体的な入力値(= 28)をL-varsに適用

```typescript
evaluate(temp, 28);
// {
//   凍えそう: 0,
//   寒い: 0,
//   暖かい: 1,
//   暑い: 0.0003353501304664781
// }
```

候補の中で妥当な変数に分類

```typescript
classify(temp, 28, 0.33));
// 暖かい
```

定義したL-varsの可視化:

```typescript
import { varToSvg } from "@thi.ng/fuzzy-viz";

fs.writeFileSync("dist/temperature.svg", varToSvg(temp, { samples: 200 }));
```

![temperature_svg_and_dist.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/577/84ed8ecd-4e0c-9c96-fa50-330c53cd06a0.png)


### 推論ルールの定義

入力変数と出力変数を組み合わせて知識ベースの推論ルールを定義することができます。このルールにより、人間の認知ベースの意思決定をシミュレートすることができます。


```typescript
const inputs = {
  食事: variable([0, 10], {
    まずい: invRamp(1, 3),
    美味しい: ramp(7, 9),
  }),
  サービス: variable([0, 10], {
    悪い: gaussian(0, 1.5),
    良い: gaussian(5, 1.5),
    素晴らしい: gaussian(10, 1.5),
  }),
};

const outputs = {
  チップ: variable([0, 30], {
    少なめ: triangle(0, 5, 10),
    普通: triangle(10, 15, 20),
    多め: triangle(20, 25, 30),
  }),
};

type I = typeof inputs;
type O = typeof outputs;

// ルール定義:
// 食事 が まずい か サービス が 悪い ならば チップ は 少なめ と推論
// サービス が 良い ならば チップ は 普通 と推論
// 食事 が 美味しい か サービス が 素晴らしい ならば チップ は 多め と推論
const rules = [
  or<I, O>({ 食事: "まずい", サービス: "悪い" }, { チップ: "少なめ" }),
  or<I, O>({ サービス: "良い" }, { チップ: "普通" }),
  or<I, O>({ 食事: "美味しい", サービス: "素晴らしい" }, { チップ: "多め" }),
];

defuzz(
  inputs,
  outputs,
  rules,
  // 入力値
  { 食事: 7.32, サービス: 7.83 }
);
// 推論結果 { チップ: 22.650000000000034 }
```


## アプリケーションに応用するとしたら

制御系のシステムでなくても、一般的なアプリケーションの振る舞いとしてルールベースの分岐を入れている箇所はいろいろあると思います。ファジィ論理への置き換えでアイディエーションしてみると

- ユーザーからの入力や、画面内の状態から振る舞い(戦略)を変える
    - インタラクション
    - 画面の表示
- 画面内の表示コンポーネント数によるパフォーマンス調整
- NPC的なアバターの操作
- カーソル移動の加速度で傾き
- ポップアップウインドウのちょうど良い配置
- 近いエリア内のオブジェクト配置の密を避ける。近いと邪魔だから避けるような振る舞い


## 参考
1. [ファジィ論理 \- Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%B8%E3%82%A3%E8%AB%96%E7%90%86)
1. [あいまいさの効用 ー ファジィ理論とその周辺 ー (pdf)](http://www-inulab.sys.es.osaka-u.ac.jp/uploads/inuiguchi/Aimai-ppt.pdf)
1. [Fuzzy Logic (pdf)](https://teaching.csse.uwa.edu.au/units/CITS7212/Lectures/Students/Fuzzy.pdf)
1. [ファジィ理論を応用した魚ロボットの運動制御](https://www.nmri.go.jp/oldpages/eng/khirata/fish/experiment/fuzzy/fuzzyj.htm)
1. [ファジー理論とは何か？AIで話題の論理をわかりやすく解説 \| ソフトウェア開発のギークフィード](https://www.geekfeed.co.jp/geekblog/fuzzy-logic/)