├── src
├── App.css
├── index.css
├── main.tsx
├── components
│ ├── visual
│ │ ├── use-window-size.tsx
│ │ ├── result-mesh.tsx
│ │ ├── pixel-plane-mesh.tsx
│ │ ├── Canvas-drawing.tsx
│ │ ├── drawLineGroup.tsx
│ │ ├── input-hidden-plane.tsx
│ │ └── mnist-sample.ts
│ ├── neuralnet
│ │ ├── covolution.tsx
│ │ ├── testData
│ │ │ └── testData.json
│ │ ├── functions.test.ts
│ │ └── functions.ts
│ └── ui
│ │ └── sidebar.tsx
├── vite-env.d.ts
├── assets
│ └── react.svg
└── App.tsx
├── .gitattributes
├── .prettierrc
├── jest.config.json
├── thumbnail.png
├── postcss.config.js
├── vite.config.ts
├── tailwind.config.js
├── tsconfig.node.json
├── .gitignore
├── .eslintrc.cjs
├── public
├── file.json
└── vite.svg
├── tsconfig.json
├── index.html
├── README.md
└── package.json
/src/App.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | neural.json filter=lfs diff=lfs merge=lfs -text
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "singleQuote": true
4 | }
5 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/jest.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "preset": "ts-jest",
3 | "testEnvironment": "node"
4 | }
5 |
--------------------------------------------------------------------------------
/thumbnail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kenjiSpecial/CNNVisualizer/HEAD/thumbnail.png
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import ReactDOM from 'react-dom/client';
2 | import App from './App.tsx';
3 | import './index.css';
4 |
5 | ReactDOM.createRoot(document.getElementById('root')!).render();
6 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | });
8 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
4 | theme: {
5 | extend: {},
6 | },
7 | plugins: [],
8 | };
9 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "include": ["vite.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: { browser: true, es2020: true },
4 | extends: [
5 | 'eslint:recommended',
6 | 'plugin:@typescript-eslint/recommended',
7 | 'plugin:react-hooks/recommended',
8 | 'plugin:prettier/recommended',
9 | ],
10 | ignorePatterns: ['dist', '.eslintrc.cjs'],
11 | parser: '@typescript-eslint/parser',
12 | plugins: ['react-refresh'],
13 | rules: {
14 | 'react-refresh/only-export-components': [
15 | 'warn',
16 | { allowConstantExport: true },
17 | ],
18 | },
19 | };
20 |
--------------------------------------------------------------------------------
/public/file.json:
--------------------------------------------------------------------------------
1 | {
2 | "glossary": {
3 | "title": "example glossary",
4 | "GlossDiv": {
5 | "title": "S",
6 | "GlossList": {
7 | "GlossEntry": {
8 | "ID": "SGML",
9 | "SortAs": "SGML",
10 | "GlossTerm": "Standard Generalized Markup Language",
11 | "Acronym": "SGML",
12 | "Abbrev": "ISO 8879:1986",
13 | "GlossDef": {
14 | "para": "A meta-markup language, used to create markup languages such as DocBook.",
15 | "GlossSeeAlso": ["GML", "XML"]
16 | },
17 | "GlossSee": "markup"
18 | }
19 | }
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "noEmit": true,
15 | "jsx": "react-jsx",
16 |
17 | /* Linting */
18 | "strict": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "noFallthroughCasesInSwitch": true
22 | },
23 | "include": ["src"],
24 | "references": [{ "path": "./tsconfig.node.json" }]
25 | }
26 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | CNNビジュアライザー
8 |
9 |
13 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/components/visual/use-window-size.tsx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 |
3 | // ウィンドウサイズを表す型定義
4 | export interface IWindowSize {
5 | width: number | undefined;
6 | height: number | undefined;
7 | }
8 |
9 | // カスタムフックの定義
10 | export const useWindowSize = (): IWindowSize => {
11 | const [windowSize, setWindowSize] = useState({
12 | width: undefined,
13 | height: undefined,
14 | });
15 |
16 | useEffect(() => {
17 | // サイズを設定するヘルパー関数
18 | const handleResize = () => {
19 | setWindowSize({
20 | width: window.innerWidth,
21 | height: window.innerHeight,
22 | });
23 | };
24 |
25 | // 初回レンダリング時にサイズを設定
26 | handleResize();
27 |
28 | // resizeイベントリスナーを設定
29 | window.addEventListener('resize', handleResize);
30 |
31 | // クリーンアップ関数
32 | return () => window.removeEventListener('resize', handleResize);
33 | }, []); // 空の依存配列でマウント時とアンマウント時のみ実行
34 |
35 | return windowSize;
36 | };
37 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module 'mnist' {
4 | type Output = [
5 | number,
6 | number,
7 | number,
8 | number,
9 | number,
10 | number,
11 | number,
12 | number,
13 | number,
14 | number,
15 | ];
16 |
17 | type Datum = {
18 | input: number[];
19 | output: Output;
20 | };
21 |
22 | interface Digit {
23 | id: number;
24 | raw: number[];
25 | length: number;
26 | get: (index?: number) => number[];
27 | range: (start: number, end: number) => number[][];
28 | set: (start: number, end: number) => Datum[];
29 | }
30 |
31 | namespace MNIST {
32 | export function set(
33 | trainingSetSize: number,
34 | testSetSize: number,
35 | ): {
36 | training: Datum[];
37 | test: Datum[];
38 | };
39 |
40 | export function get(count: number): Datum[];
41 |
42 | export function draw(
43 | digit: number[],
44 | context: CanvasRenderingContext2D,
45 | offsetX: number,
46 | offsetY: number,
47 | ): void;
48 |
49 | export function toNumber(array: number[]): number;
50 | }
51 |
52 | export = MNIST as typeof MNIST & Digit[];
53 | }
54 |
55 | // declare module 'numjs' {
56 | // export interface NdArray {
57 | // log: () => NdArray;
58 | // }
59 | // }
60 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CNNビジュアライザー
2 |
3 | [](https://ks-cnnvisualizer.netlify.app/)
4 | URL: https://ks-cnnvisualizer.netlify.app/
5 |
6 | ## 概要
7 |
8 | CNNビジュアライザーは、畳み込みニューラルネットワーク(CNN)を可視化するサイトです。
9 |
10 | [「ゼロから作るDeep Learning」](https://www.amazon.co.jp/dp/4873117585?ref_=cm_sw_r_cp_ud_dp_D4WTQD6YZC7XPRNG5K9V) でデモ学習として作り、学習してできたCNNモデルを利用しています。
11 |
12 | 理論的な説明は[「ゼロから作るDeep Learning」](https://www.amazon.co.jp/dp/4873117585?ref_=cm_sw_r_cp_ud_dp_D4WTQD6YZC7XPRNG5K9V) を読んでいただけると、理解は深まると思います。
13 |
14 | CNNビジュアライザーでは入力層、畳み込み層+プーリング層、全結合層、出力層の4つの層に分けています。スケッチキャンパスに自分で絵を描いてみると、文字を認識してくれます。
15 |
16 | このプロジェクトは、TypeScript、React、およびReact Three Fiber(r3f)を使用して開発されています。ニューラルネットワークの複雑な構造とそのパラメータを直感的に理解するのに役立ちます。
17 |
18 | # プレビュー
19 |
20 | プロジェクトのプレビューはこちらからご覧いただけます: https://ks-cnnvisualizer.netlify.app/
21 |
22 | ## 開発とビルド
23 |
24 | ### プロジェクトの開発
25 |
26 | ローカルでの開発を行うには、以下のコマンドを実行します:
27 |
28 | ```
29 | $ vite dev
30 | ```
31 |
32 | このコマンドにより、開発用サーバが起動し、リアルタイムでのコード変更が反映されます。
33 |
34 | ### プロジェクトのビルド
35 |
36 | プロジェクトをビルドして本番環境にデプロイするには、以下のコマンドを実行します:
37 |
38 | ```
39 | vite build
40 | ```
41 |
42 | このコマンドにより、本番用の最適化されたビルドが生成されます。
43 |
44 | ## コントリビュート
45 |
46 | プロジェクトへの貢献に興味がある方は、issueやプルリクエストを通じてご参加ください。すべての貢献は大歓迎です!
47 |
48 | ## ライセンス
49 |
50 | このプロジェクトはMITライセンスの下で公開されています。
51 |
52 | ## 関連
53 |
54 | - [kenjiSpecial/NNVisualizer](https://github.com/kenjiSpecial/NNVisualizer)
55 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-ts",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite --port 3000",
8 | "build": "tsc && vite build",
9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview",
11 | "test": "jest"
12 | },
13 | "dependencies": {
14 | "@react-three/drei": "^9.88.17",
15 | "@react-three/fiber": "^8.15.11",
16 | "@types/three": "^0.158.3",
17 | "leva": "^0.9.35",
18 | "react": "^18.2.0",
19 | "react-dom": "^18.2.0",
20 | "three": "^0.158.0"
21 | },
22 | "devDependencies": {
23 | "@types/jest": "^29.5.10",
24 | "@types/numjs": "^0.16.7",
25 | "@types/react": "^18.2.37",
26 | "@types/react-dom": "^18.2.15",
27 | "@typescript-eslint/eslint-plugin": "^6.10.0",
28 | "@typescript-eslint/parser": "^6.10.0",
29 | "@vitejs/plugin-react": "^4.2.0",
30 | "autoprefixer": "^10.4.16",
31 | "eslint": "^8.53.0",
32 | "eslint-config-prettier": "^9.0.0",
33 | "eslint-plugin-prettier": "^5.0.1",
34 | "eslint-plugin-react-hooks": "^4.6.0",
35 | "eslint-plugin-react-refresh": "^0.4.4",
36 | "jest": "^29.7.0",
37 | "postcss": "^8.4.31",
38 | "prettier": "^3.1.0",
39 | "tailwindcss": "^3.3.5",
40 | "ts-jest": "^29.1.1",
41 | "typescript": "^5.2.2",
42 | "vite": "^5.0.0"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/visual/result-mesh.tsx:
--------------------------------------------------------------------------------
1 | import { Text } from '@react-three/drei';
2 | import { useMemo } from 'react';
3 | import { Vector3 } from 'three';
4 |
5 | type ResultMeshProps = JSX.IntrinsicElements['group'] & {
6 | result: number[] | undefined;
7 | pos: Vector3;
8 | };
9 |
10 | export function ResultMesh(props: ResultMeshProps) {
11 | // result[0]が存在するとき、result[0]のarrayをsoftmax関数にかける。
12 | // softmax関数をかけた結果の配列を返す。
13 | // result[0]が存在しないとき、undefinedを返す。
14 | // useMemoを使って、キャッシュする。
15 |
16 | // console.log(softmaxArr);
17 |
18 | // groupを作成する。groupの中にTextを作成する。textは0-9の数字を表示する。
19 | // useMemoを使って、キャッシュする。
20 | const textGroup = useMemo(() => {
21 | const textArr = [];
22 |
23 | for (let i = 0; i < 10; i++) {
24 | const theta = (i * Math.PI) / 5 - Math.PI / 2;
25 | const rad = 8;
26 | const x = Math.cos(theta) * rad;
27 | const y = -Math.sin(theta) * rad;
28 | const z = 0;
29 | const pos = new Vector3(x, y, z);
30 | const fontSize =
31 | props.result && props.result[i]
32 | ? 7 * Math.max(props.result[i], 0.05)
33 | : 0.05;
34 | textArr.push(
35 | {`${i}`},
41 | );
42 | }
43 | return textArr;
44 | }, [props.result]);
45 |
46 | return {textGroup};
47 | }
48 |
--------------------------------------------------------------------------------
/src/components/neuralnet/covolution.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 | import {
3 | calcRelu4DArray,
4 | calculationCovolution,
5 | calculationPooling,
6 | } from './functions';
7 |
8 | export const useCovolutionMemo = (
9 | inputData: number[][][], // 3次元配列 [batchSize][channel][row * col]
10 | paramsW1: number[][][][] | undefined,
11 | paramsB1: number[] | undefined,
12 | inputRowSize: number,
13 | inputColSize: number,
14 | filterNum: number,
15 | filterRowSize: number,
16 | filterColSize: number,
17 | covolutionPadding: number,
18 | covolutionStride: number,
19 | ) => {
20 | return useMemo(() => {
21 | // const outputData = inputData.map((data, index) => {
22 | // const row = Math.floor(index / filterRowSize);
23 | // const col = index % filterColSize;
24 | // const filter = filterNum * filterRowSize * filterColSize;
25 | // const filterIndex = filter + row * filterColSize + col;
26 | // const filterData = inputData[filterIndex];
27 | // return data * filterData;
28 | // });
29 | // setOutputData(outputData);
30 | if (!paramsW1 || !paramsB1) return undefined;
31 | const out = calculationCovolution(
32 | inputData,
33 | inputRowSize,
34 | inputColSize,
35 | paramsW1,
36 | paramsB1,
37 | filterNum,
38 | filterRowSize,
39 | filterColSize,
40 | covolutionPadding,
41 | covolutionStride,
42 | );
43 | return calcRelu4DArray(out);
44 | }, [
45 | inputData,
46 | paramsW1,
47 | paramsB1,
48 | inputRowSize,
49 | inputColSize,
50 | filterNum,
51 | filterRowSize,
52 | filterColSize,
53 | covolutionPadding,
54 | covolutionStride,
55 | ]);
56 | };
57 |
58 | export const usePoolingMemo = (
59 | inputData: number[][][][] | undefined,
60 | poolingRowSize: number,
61 | poolingColsize: number,
62 | poolingStride: number,
63 | PoolingPadding: number,
64 | ) => {
65 | return useMemo(() => {
66 | if (!inputData) return undefined;
67 | const inputFilterNum = inputData[0].length;
68 | const inputRowSize = inputData[0][0].length;
69 | const inputColSize = inputData[0][0][0].length;
70 |
71 | return calculationPooling(
72 | inputData,
73 | inputFilterNum,
74 | inputRowSize,
75 | inputColSize,
76 | poolingRowSize,
77 | poolingColsize,
78 | poolingStride,
79 | PoolingPadding,
80 | );
81 | }, [
82 | inputData,
83 | poolingRowSize,
84 | poolingColsize,
85 | poolingStride,
86 | PoolingPadding,
87 | ]);
88 | };
89 |
--------------------------------------------------------------------------------
/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/ui/sidebar.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useMemo, useRef } from 'react';
2 | import { CanvasDrawing } from '../visual/Canvas-drawing';
3 | import { IWindowSize } from '../visual/use-window-size';
4 |
5 | interface SidebarProps {
6 | displaySideBar: string;
7 | setInputData: (inputData: number[]) => void;
8 | inputData: number[];
9 | isButtonClicked: React.MutableRefObject;
10 | windowSize: IWindowSize;
11 | clickbutton: () => void;
12 | }
13 |
14 | const Sidebar: React.FC = ({
15 | displaySideBar,
16 | setInputData,
17 | inputData,
18 | isButtonClicked,
19 | windowSize,
20 | clickbutton,
21 | }) => {
22 | const ref = useRef(null);
23 | // モバイル用のキャプション機能をuseStateで作成する。
24 | const [isMobileCaptionOpen, setIsMobileCaptionOpen] = React.useState(false);
25 | const displayProp = useMemo(() => {
26 | return isMobileCaptionOpen ? 'block' : 'hidden';
27 | }, [isMobileCaptionOpen]);
28 | const menuText = useMemo(() => {
29 | return isMobileCaptionOpen ? '閉じる' : 'CNNビジュアライザーについて';
30 | }, [isMobileCaptionOpen]);
31 |
32 | const clickMenuButton = useCallback(() => {
33 | setIsMobileCaptionOpen(!isMobileCaptionOpen);
34 | }, [isMobileCaptionOpen]);
35 |
36 | return (
37 |
50 | {/* スマホ用ボタンを作成する */}
51 |
52 |
56 | [{menuText}]
57 |
58 |
59 |
60 |
63 |
64 |
65 |
66 |
71 | ゼロから作るDeep Learning
72 |
73 | の第7章の畳み込みニューラルネットワーク(CNN)をリアルタイムに可視化しています。ビジュアライザーでは入力層
74 | 、畳み込み層+プーリング層、全結合層、出力層の4つの層に分けています。
75 |
76 |
77 | 畳み込み層とプーリング層では、画像の特徴を抽出しています。シンプルなニューラルネットワークを可視化した
78 |
83 | NNビジュアライザー
84 |
85 | と比較してみると、畳み込みがどのように変化しているかがわかります。
86 |
87 |
88 |
98 |
99 |
100 |
101 |
102 |
103 | スケッチキャンバス
104 |
105 |
106 |
107 |
114 |
115 |
116 |
122 |
123 |
124 |
125 |
126 | スケッチキャンバスにスケッチをするか、画像テストボタンを押してください。自動でニューラルネットワークが推論を行います。
127 |
128 |
129 |
142 |
143 |
144 | );
145 | };
146 |
147 | export default Sidebar;
148 |
--------------------------------------------------------------------------------
/src/components/neuralnet/testData/testData.json:
--------------------------------------------------------------------------------
1 | {"testData": [[[[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.3294117748737335, 0.7254902124404907, 0.6235294342041016, 0.5921568870544434, 0.23529411852359772, 0.1411764770746231, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.8705882430076599, 0.9960784316062927, 0.9960784316062927, 0.9960784316062927, 0.9960784316062927, 0.9450980424880981, 0.7764706015586853, 0.7764706015586853, 0.7764706015586853, 0.7764706015586853, 0.7764706015586853, 0.7764706015586853, 0.7764706015586853, 0.7764706015586853, 0.6666666865348816, 0.20392157137393951, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.26274511218070984, 0.4470588266849518, 0.2823529541492462, 0.4470588266849518, 0.6392157077789307, 0.8901960849761963, 0.9960784316062927, 0.8823529481887817, 0.9960784316062927, 0.9960784316062927, 0.9960784316062927, 0.9803921580314636, 0.8980392217636108, 0.9960784316062927, 0.9960784316062927, 0.5490196347236633, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.06666667014360428, 0.25882354378700256, 0.054901961237192154, 0.26274511218070984, 0.26274511218070984, 0.26274511218070984, 0.23137255012989044, 0.08235294371843338, 0.9254902005195618, 0.9960784316062927, 0.4156862795352936, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.32549020648002625, 0.9921568632125854, 0.8196078538894653, 0.07058823853731155, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.08627451211214066, 0.9137254953384399, 1.0, 0.32549020648002625, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5058823823928833, 0.9960784316062927, 0.9333333373069763, 0.1725490242242813, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.23137255012989044, 0.9764705896377563, 0.9960784316062927, 0.24313725531101227, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5215686559677124, 0.9960784316062927, 0.7333333492279053, 0.019607843831181526, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.03529411926865578, 0.8039215803146362, 0.9725490212440491, 0.22745098173618317, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.4941176474094391, 0.9960784316062927, 0.7137255072593689, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.29411765933036804, 0.9843137264251709, 0.9411764740943909, 0.2235294133424759, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.07450980693101883, 0.8666666746139526, 0.9960784316062927, 0.6509804129600525, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0117647061124444, 0.7960784435272217, 0.9960784316062927, 0.8588235378265381, 0.13725490868091583, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.14901961386203766, 0.9960784316062927, 0.9960784316062927, 0.3019607961177826, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.12156862765550613, 0.8784313797950745, 0.9960784316062927, 0.45098039507865906, 0.003921568859368563, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5215686559677124, 0.9960784316062927, 0.9960784316062927, 0.20392157137393951, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.239215686917305, 0.9490196108818054, 0.9960784316062927, 0.9960784316062927, 0.20392157137393951, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.4745098054409027, 0.9960784316062927, 0.9960784316062927, 0.8588235378265381, 0.1568627506494522, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.4745098054409027, 0.9960784316062927, 0.8117647171020508, 0.07058823853731155, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]]]}
--------------------------------------------------------------------------------
/src/components/visual/pixel-plane-mesh.tsx:
--------------------------------------------------------------------------------
1 | import { DoubleSide, Texture } from 'three';
2 |
3 | type PixelPlaneProps = JSX.IntrinsicElements['group'] & {
4 | data: number[];
5 | size: number;
6 | space: number;
7 | rowSize: number;
8 | colSize: number;
9 | };
10 |
11 | export function inputPos(props: {
12 | index: number;
13 | rowSize: number;
14 | halfRowSize: number;
15 | size: number;
16 | space: number;
17 | }) {
18 | const { index, rowSize, halfRowSize, size, space } = props;
19 | const posX = ((index % rowSize) - halfRowSize) * (size + space);
20 | const posY = (-Math.floor(index / rowSize) + halfRowSize) * (size + space);
21 | const posZ = 0;
22 | return { posX, posY, posZ };
23 | }
24 |
25 | export function PixelPlaneMesh(props: PixelPlaneProps) {
26 | // const data = [1, 2, 3, 4];
27 | const data = props.data;
28 | const { size, space, rowSize } = props;
29 | const halfRowSize = rowSize / 2;
30 |
31 | return (
32 |
33 | {data.map((d, index) => {
34 | const colorVal = Math.floor(d * 255);
35 | const color = `rgb(${colorVal}, ${colorVal}, ${colorVal})`;
36 |
37 | const { posX, posY, posZ } = inputPos({
38 | index,
39 | rowSize,
40 | halfRowSize,
41 | size,
42 | space,
43 | });
44 | const key = `plane-${index}`;
45 |
46 | return (
47 |
52 |
53 |
54 |
55 | );
56 | })}
57 |
58 | );
59 | }
60 |
61 | type ParamsPixelPlaneProps = JSX.IntrinsicElements['group'] & {
62 | hiddenSize: number;
63 | hiddenValueArr: number[];
64 | size: number;
65 | space: number;
66 | rowSize: number;
67 | colSize: number;
68 | };
69 |
70 | export function hiddenPos(props: {
71 | index: number;
72 | rowSize: number;
73 | colSize: number;
74 | size: number;
75 | space: number;
76 | }) {
77 | const { index, rowSize, colSize, size, space } = props;
78 | const posX = ((index % rowSize) - (rowSize - 1) / 2) * (size + space);
79 | const posY =
80 | (-Math.floor(index / rowSize) + (colSize - 1) / 2) * (size + space);
81 | const posZ = 0;
82 | return { posX, posY, posZ };
83 | }
84 |
85 | export function ParamsPixelPlaneMesh(props: ParamsPixelPlaneProps) {
86 | const arr = [];
87 |
88 | const { size, space } = props;
89 | const rowSize = props.rowSize;
90 | const colSize = props.colSize;
91 |
92 | for (let i = 0; i < props.hiddenSize; i++) {
93 | const colorVal = Math.floor(props.hiddenValueArr[i] * 255);
94 | const color = `rgb(${colorVal}, ${colorVal}, ${colorVal})`;
95 |
96 | const { posX, posY, posZ } = hiddenPos({
97 | index: i,
98 | rowSize,
99 | colSize,
100 | size,
101 | space,
102 | });
103 | const key = `plane-${i}`;
104 | arr.push(
105 |
106 |
107 |
108 | ,
109 | );
110 | }
111 | return {arr};
112 | }
113 |
114 | type OutputMeshProps = JSX.IntrinsicElements['group'] & {
115 | outputSize: number;
116 | outputValueArr: number[];
117 | size: number;
118 | space: number;
119 | rowsize: number;
120 | colSize: number;
121 | };
122 |
123 | export function outputPos(props: {
124 | index: number;
125 | rowSize: number;
126 | colSize: number;
127 | size: number;
128 | space: number;
129 | }) {
130 | const { index, rowSize, size, space, colSize } = props;
131 | const newIndex = index === 0 ? 9 : index - 1;
132 | const posX = ((newIndex % rowSize) - (rowSize - 1) / 2) * (size + space);
133 | const posY =
134 | (-Math.floor(newIndex / rowSize) + (colSize - 1) / 2) * (size + space);
135 | const posZ = 0;
136 | return { posX, posY, posZ };
137 | }
138 |
139 | export function OutputMeshGroup(props: OutputMeshProps) {
140 | const outputMeshArr = [];
141 | const { size, space, rowsize } = props;
142 | for (let ii = 0; ii < props.outputSize; ii++) {
143 | const colorVal = Math.floor(props.outputValueArr[ii] * 255);
144 | const color = `rgb(${colorVal}, ${colorVal}, ${colorVal})`;
145 | const { posX, posY, posZ } = outputPos({
146 | index: ii,
147 | rowSize: rowsize,
148 | colSize: props.colSize,
149 | size,
150 | space,
151 | });
152 | const canvas = document.createElement('canvas');
153 | canvas.width = 64;
154 | canvas.height = 64;
155 | const ctx = canvas.getContext('2d');
156 | if (ctx) {
157 | ctx.fillStyle = color;
158 | ctx.fillRect(0, 0, 64, 64);
159 | ctx.fillStyle = 'black';
160 | ctx.font = '40px san-serif';
161 | ctx.textAlign = 'center';
162 | ctx.textBaseline = 'middle';
163 | ctx.fillText(`${ii}`, 32, 32);
164 | }
165 | const texture = new Texture(canvas);
166 | texture.needsUpdate = true;
167 | const key = `plane-${ii}`;
168 | outputMeshArr.push(
169 |
170 |
171 |
172 | ,
173 | );
174 | }
175 |
176 | return {outputMeshArr};
177 | }
178 |
--------------------------------------------------------------------------------
/src/components/visual/Canvas-drawing.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef, useEffect, useMemo } from 'react';
2 |
3 | export const CanvasDrawing: React.FC<{
4 | setInputData: (data: number[]) => void;
5 | inputData: number[];
6 | isButtonClicked: React.MutableRefObject;
7 | windowWidth: number | undefined;
8 | parentRef: React.RefObject;
9 | }> = ({ setInputData, inputData, isButtonClicked, windowWidth, parentRef }) => {
10 | const canvasRef = useRef(null);
11 | // 28 * 28の2D Contextを作成する
12 | const vContext = useMemo(() => {
13 | const vCanvas = document.createElement('canvas');
14 | vCanvas.width = 28;
15 | vCanvas.height = 28;
16 | return vCanvas.getContext('2d', { willReadFrequently: true });
17 | }, []);
18 | const pt = useRef({ x: 0, y: 0 });
19 |
20 | useEffect(() => {
21 | const canvas = canvasRef.current;
22 | if (!canvas) return;
23 | const context = canvas.getContext('2d', { willReadFrequently: true });
24 | if (!context) return;
25 | if (!vContext) return;
26 | const data = vContext.getImageData(0, 0, 28, 28);
27 | for (let i = 0; i < data.data.length; i++) {
28 | const val = Math.floor(255 * inputData[i]);
29 | data.data[4 * i] = val; //Math.floor(inputData[i] * 255);
30 | data.data[4 * i + 1] = val; //inputData[i] * 255;
31 | data.data[4 * i + 2] = val; //inputData[i] * 255;
32 | data.data[4 * i + 3] = 255; //inputData[i] * 255;
33 | }
34 | vContext.putImageData(data, 0, 0);
35 | context.drawImage(vContext.canvas, 0, 0, canvas.width, canvas.height);
36 | }, []);
37 |
38 | useEffect(() => {
39 | if (!isButtonClicked.current) return;
40 |
41 | const canvas = canvasRef.current;
42 | if (!canvas) return;
43 | const context = canvas.getContext('2d', { willReadFrequently: true });
44 | if (!context) return;
45 | if (!vContext) return;
46 | const data = vContext.getImageData(0, 0, 28, 28);
47 | for (let i = 0; i < data.data.length; i++) {
48 | const val = Math.floor(255 * inputData[i]);
49 | data.data[4 * i] = val; //Math.floor(inputData[i] * 255);
50 | data.data[4 * i + 1] = val; //inputData[i] * 255;
51 | data.data[4 * i + 2] = val; //inputData[i] * 255;
52 | data.data[4 * i + 3] = 255; //inputData[i] * 255;
53 | }
54 | vContext.putImageData(data, 0, 0);
55 | context.drawImage(vContext.canvas, 0, 0, canvas.width, canvas.height);
56 |
57 | isButtonClicked.current = false;
58 | }, [isButtonClicked.current, inputData, canvasRef.current, vContext]);
59 |
60 | useEffect(() => {
61 | const canvas = canvasRef.current;
62 | if (!canvas) return;
63 |
64 | const context = canvas.getContext('2d');
65 | if (!context) return;
66 | // canvasをblackで塗りつぶす
67 |
68 | let isDrawing = false;
69 | let lastX = 0;
70 | let lastY = 0;
71 |
72 | const startDrawing = (event: MouseEvent | TouchEvent) => {
73 | isDrawing = true;
74 | const { clientX, clientY } =
75 | event instanceof MouseEvent ? event : event.touches[0];
76 | const rect = canvas.getBoundingClientRect();
77 | lastX = clientX - rect.left;
78 | lastY = clientY - rect.top;
79 |
80 | pt.current = { x: lastX, y: lastY };
81 |
82 | context.fillStyle = 'black';
83 | context.fillRect(0, 0, canvas.width, canvas.height);
84 |
85 | if (event instanceof TouchEvent) {
86 | event.preventDefault();
87 | }
88 | };
89 |
90 | const draw = (event: MouseEvent | TouchEvent) => {
91 | if (!isDrawing) return;
92 | const { clientX, clientY } =
93 | event instanceof MouseEvent ? event : event.touches[0];
94 | const rect = canvas.getBoundingClientRect();
95 | const x = clientX - rect.left;
96 | const y = clientY - rect.top;
97 | pt.current = { x, y };
98 |
99 | // 滑らかに線を描画する
100 | context.lineJoin = 'round';
101 | context.lineCap = 'round';
102 |
103 | context.beginPath();
104 | context.moveTo(lastX, lastY);
105 | context.lineTo(x, y);
106 | context.strokeStyle = 'white';
107 | context.lineWidth = event instanceof MouseEvent ? 40 : 20;
108 | context.stroke();
109 | context.closePath();
110 |
111 | lastX = x;
112 | lastY = y;
113 |
114 | // 28 * 28にリサイズする
115 | vContext!.drawImage(canvas, 0, 0, 28, 28);
116 | // 28 * 28のImageDataを取得する
117 | const imageData = vContext!.getImageData(0, 0, 28, 28);
118 | // 28 * 28のImageDataを白黒データを正規規化して、入力データにする
119 | // 配列のおおきさは 28 * 28 = 784このデータが入力データになる
120 | const normalizedData = Array.from(imageData.data)
121 | .filter((_, index) => index % 4 === 0)
122 | .map((v) => v / 255);
123 |
124 | setInputData(normalizedData);
125 |
126 | if (event instanceof TouchEvent) {
127 | event.preventDefault();
128 | }
129 | };
130 |
131 | const stopDrawing = () => {
132 | isDrawing = false;
133 | };
134 |
135 | canvas.addEventListener('mousedown', startDrawing);
136 | canvas.addEventListener('touchstart', startDrawing);
137 | canvas.addEventListener('mousemove', draw);
138 | canvas.addEventListener('touchmove', draw);
139 | canvas.addEventListener('mouseup', stopDrawing);
140 | canvas.addEventListener('touchend', stopDrawing);
141 | // resize
142 |
143 | return () => {
144 | canvas.removeEventListener('mousedown', startDrawing);
145 | canvas.removeEventListener('touchstart', startDrawing);
146 | canvas.removeEventListener('mousemove', draw);
147 | canvas.removeEventListener('touchmove', draw);
148 | canvas.removeEventListener('mouseup', stopDrawing);
149 | canvas.removeEventListener('touchend', stopDrawing);
150 | };
151 | }, []);
152 |
153 | const canvasWidth = useMemo(() => {
154 | // console.log(parentRef.current?.offsetWidth);
155 | if (!windowWidth) {
156 | return window.innerWidth < 640 ? 200 : 320;
157 | }
158 | if (windowWidth < 640) {
159 | return 200;
160 | } else {
161 | return 320;
162 | }
163 | }, [windowWidth, parentRef]);
164 |
165 | return (
166 | <>
167 |
173 | >
174 | );
175 | };
176 |
--------------------------------------------------------------------------------
/src/components/neuralnet/functions.test.ts:
--------------------------------------------------------------------------------
1 | import {
2 | calcAffine,
3 | calcRelu2DArray,
4 | calcRelu4DArray,
5 | calcSoftMax,
6 | calculationCovolution,
7 | calculationPooling,
8 | reshapeArray,
9 | reshapeImageArray,
10 | // reshapeArray3to1,
11 | } from './functions'; // ここにテスト対象の関数をimportする
12 |
13 | // eslint-disable-next-line @typescript-eslint/no-var-requires
14 | const testSample = require('./testData/testData.json') as unknown as {
15 | testData: number[][][][];
16 | };
17 |
18 | // eslint-disable-next-line @typescript-eslint/no-var-requires
19 | const cnnParams = require('../../../public/cnn-params.json') as unknown as {
20 | W1: number[][][][];
21 | b1: number[];
22 | W2: number[][];
23 | b2: number[];
24 | W3: number[][];
25 | b3: number[];
26 | };
27 |
28 | test('covolution', () => {
29 | const inputData = [[[1, 2, 3, 4, 5, 6, 7, 8, 9]]];
30 | const inputRowSize = 3;
31 | const inputColSize = 3;
32 | const paramsW1 = [
33 | [
34 | [
35 | [1, 2],
36 | [3, 4],
37 | ],
38 | ],
39 | [
40 | [
41 | [5, 6],
42 | [7, 8],
43 | ],
44 | ],
45 | ];
46 | const paramsB1 = [1, 2];
47 | const filterNum = 2;
48 | const filterRowSize = 2;
49 | const filterColSize = 2;
50 | const covolutionPadding = 0;
51 | const covolutionStride = 1;
52 | const result = calculationCovolution(
53 | inputData,
54 | inputRowSize,
55 | inputColSize,
56 | paramsW1,
57 | paramsB1,
58 | filterNum,
59 | filterRowSize,
60 | filterColSize,
61 | covolutionPadding,
62 | covolutionStride,
63 | );
64 | const expected = [
65 | [
66 | [
67 | [1 * 1 + 2 * 2 + 4 * 3 + 5 * 4 + 1, 2 * 1 + 3 * 2 + 5 * 3 + 6 * 4 + 1],
68 | [4 * 1 + 5 * 2 + 7 * 3 + 8 * 4 + 1, 5 * 1 + 6 * 2 + 8 * 3 + 9 * 4 + 1],
69 | ],
70 | [
71 | [1 * 5 + 2 * 6 + 4 * 7 + 5 * 8 + 2, 2 * 5 + 3 * 6 + 5 * 7 + 6 * 8 + 2],
72 | [4 * 5 + 5 * 6 + 7 * 7 + 8 * 8 + 2, 5 * 5 + 6 * 6 + 8 * 7 + 9 * 8 + 2],
73 | ],
74 | ],
75 | ];
76 |
77 | expect(result).toEqual(expected);
78 | });
79 |
80 | it('calculationPoolingのテスト', () => {
81 | const inputData = [
82 | [
83 | [
84 | [1, 2, 3, 4],
85 | [5, 6, 7, 8],
86 | [9, 10, 11, 12],
87 | [13, 14, 15, 16],
88 | ],
89 | [
90 | [17, 18, 19, 20],
91 | [21, 22, 23, 24],
92 | [25, 26, 27, 28],
93 | [29, 30, 31, 32],
94 | ],
95 | ],
96 | ];
97 |
98 | const inputRowSize = 4;
99 | const inputColSize = 4;
100 | const filterNum = 2;
101 | const poolingStride = 2;
102 | const poolingPadding = 0;
103 | const poolingRowSize = 2;
104 | const poolingColSize = 2;
105 | const result = calculationPooling(
106 | inputData,
107 | filterNum,
108 | inputRowSize,
109 | inputColSize,
110 | poolingRowSize,
111 | poolingColSize,
112 | poolingStride,
113 | poolingPadding,
114 | );
115 |
116 | const expected = [
117 | [
118 | [
119 | [6, 8],
120 | [14, 16],
121 | ],
122 | [
123 | [22, 24],
124 | [30, 32],
125 | ],
126 | ],
127 | ];
128 |
129 | expect(result).toEqual(expected);
130 | });
131 |
132 | it('reshapeArrayのテスト', () => {
133 | const array = [
134 | [
135 | [
136 | [1, 2, 3],
137 | [4, 5, 6],
138 | [7, 8, 9],
139 | ],
140 | [
141 | [1, 2, 3],
142 | [4, 5, 6],
143 | [7, 8, 9],
144 | ],
145 | ],
146 | ];
147 |
148 | const result = reshapeArray(array);
149 |
150 | const expected = [[1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9]];
151 |
152 | expect(result).toEqual(expected);
153 | });
154 |
155 | it('covolutionのテスト', () => {
156 | const testData = reshapeImageArray(testSample.testData);
157 |
158 | // batch size
159 | expect(testData.length).toBe(1);
160 | // color channel size
161 | expect(testData[0].length).toBe(1);
162 | // image data size
163 | expect(testData[0][0].length).toBe(28 * 28);
164 |
165 | const W1 = cnnParams.W1;
166 | const b1 = cnnParams.b1;
167 | // const W2 = cnnParams.W2;
168 |
169 | const calcValue1 = calculationCovolution(
170 | testData,
171 | 28,
172 | 28,
173 | W1,
174 | b1,
175 | 30,
176 | 5,
177 | 5,
178 | 0,
179 | 1,
180 | );
181 | expect(calcValue1.length).toBe(1);
182 | expect(calcValue1[0].length).toBe(30);
183 | expect(calcValue1[0][0].length).toBe(24);
184 | expect(calcValue1[0][0][0].length).toBe(24);
185 | expect(calcValue1[0][0][0][0]).toBeCloseTo(-0.2849);
186 |
187 | const calcValue2 = calcRelu4DArray(calcValue1);
188 | expect(calcValue2.length).toBe(1);
189 | expect(calcValue2[0].length).toBe(30);
190 | expect(calcValue2[0][0].length).toBe(24);
191 | expect(calcValue2[0][0][0].length).toBe(24);
192 | expect(calcValue2[0][0][0][0]).toEqual(0);
193 | expect(calcValue2[0][0][4][4]).toBeCloseTo(0.053);
194 |
195 | const calcValue3 = calculationPooling(calcValue2, 30, 24, 24, 2, 2, 2, 0);
196 | expect(calcValue3.length).toBe(1);
197 | expect(calcValue3[0].length).toBe(30);
198 | expect(calcValue3[0][0].length).toBe(12);
199 | expect(calcValue3[0][0][0].length).toBe(12);
200 | expect(calcValue3[0][0][0][0]).toEqual(0);
201 | expect(calcValue3[0][0][2][2]).toBeCloseTo(0.063);
202 |
203 | const calcValue4 = reshapeArray(calcValue3);
204 | expect(calcValue4.length).toBe(1);
205 | expect(calcValue4[0].length).toBe(30 * 12 * 12);
206 |
207 | const calcValue5 = calcAffine(calcValue4, cnnParams.W2, cnnParams.b2);
208 | expect(calcValue5.length).toBe(1);
209 | expect(calcValue5[0].length).toBe(100);
210 | expect(calcValue5[0][0]).toBeCloseTo(3.924);
211 |
212 | const calcValue6 = calcRelu2DArray(calcValue5);
213 | expect(calcValue6.length).toBe(1);
214 | expect(calcValue6[0].length).toBe(100);
215 | expect(calcValue6[0][0]).toBeCloseTo(3.924);
216 | expect(calcValue6[0][2]).toEqual(0);
217 |
218 | const calcValue7 = calcAffine(calcValue6, cnnParams.W3, cnnParams.b3);
219 | expect(calcValue7.length).toBe(1);
220 | expect(calcValue7[0].length).toBe(10);
221 |
222 | // calcValue7の最大値のインデックスを取得する
223 | let maxIndex = 0;
224 | let maxValue = calcValue7[0][0];
225 | for (let i = 1; i < calcValue7[0].length; i++) {
226 | if (calcValue7[0][i] > maxValue) {
227 | maxIndex = i;
228 | maxValue = calcValue7[0][i];
229 | }
230 | }
231 |
232 | expect(maxIndex).toBe(7);
233 |
234 | const calcValue8 = calcSoftMax(calcValue7);
235 | expect(calcValue8.length).toBe(1);
236 | expect(calcValue8[0].length).toBe(10);
237 | expect(calcValue8[0][7]).toBeCloseTo(1);
238 | });
239 |
--------------------------------------------------------------------------------
/src/components/neuralnet/functions.ts:
--------------------------------------------------------------------------------
1 | export const calculationCovolution = (
2 | inputData: number[][][],
3 | inputRowSize: number,
4 | inputColSize: number,
5 | paramsW1: number[][][][],
6 | paramsB1: number[],
7 | filterNum: number,
8 | filterRowSize: number,
9 | filterColSize: number,
10 | covolutionPadding: number,
11 | covolutionStride: number,
12 | ) => {
13 | // ここに処理を書く
14 | const covolutionData = [];
15 | const batchSize = inputData.length;
16 | const channelSize = inputData[0].length;
17 | const outputRowSize =
18 | (inputRowSize + 2 * covolutionPadding - filterRowSize) / covolutionStride +
19 | 1;
20 | const outputColSize =
21 | (inputColSize + 2 * covolutionPadding - filterColSize) / covolutionStride +
22 | 1;
23 |
24 | // 畳み込み演算を行う
25 | for (let b = 0; b < batchSize; b++) {
26 | const batchData = [];
27 | for (let n = 0; n < filterNum; n++) {
28 | const filterData = [];
29 | for (let i = 0; i < outputRowSize; i++) {
30 | const rowData = [];
31 | for (let j = 0; j < outputColSize; j++) {
32 | let sum = 0;
33 | for (let m = 0; m < channelSize; m++) {
34 | for (let k = 0; k < filterRowSize; k++) {
35 | for (let l = 0; l < filterColSize; l++) {
36 | const row = i * covolutionStride + k - covolutionPadding;
37 | const col = j * covolutionStride + l - covolutionPadding;
38 | if (
39 | row >= 0 &&
40 | row < inputRowSize &&
41 | col >= 0 &&
42 | col < inputColSize
43 | ) {
44 | const index = row * inputColSize + col;
45 | sum += inputData[b][m][index] * paramsW1[n][m][k][l];
46 | }
47 | }
48 | }
49 | }
50 | rowData.push(sum + paramsB1[n]);
51 | }
52 | filterData.push(rowData);
53 | }
54 | batchData.push(filterData);
55 | }
56 | covolutionData.push(batchData);
57 | }
58 |
59 | return covolutionData;
60 | };
61 |
62 | // プーリング層の計算を行う
63 | export const calculationPooling = (
64 | inputData: number[][][][],
65 | filterNum: number,
66 | inputRowSize: number,
67 | inputColSize: number,
68 | poolingRowSize: number,
69 | poolingColSize: number,
70 | poolingStride: number,
71 | poolingPadding: number,
72 | ) => {
73 | // ここにプーリング層の計算を書く
74 | const poolingData = [];
75 | const outputRowSize =
76 | (inputRowSize + 2 * poolingPadding - poolingRowSize) / poolingStride + 1;
77 | const outputColSize =
78 | (inputColSize + 2 * poolingPadding - poolingColSize) / poolingStride + 1;
79 | const batchSize = inputData.length;
80 |
81 | // プーリング演算を行う (Maxプーリング)
82 |
83 | for (let b = 0; b < batchSize; b++) {
84 | const batchData = [];
85 | for (let n = 0; n < filterNum; n++) {
86 | const filterData = [];
87 | for (let i = 0; i < outputRowSize; i++) {
88 | const rowData = [];
89 | for (let j = 0; j < outputColSize; j++) {
90 | let max = -Infinity;
91 | for (let k = 0; k < poolingRowSize; k++) {
92 | for (let l = 0; l < poolingColSize; l++) {
93 | const row = i * poolingStride + k - poolingPadding;
94 | const col = j * poolingStride + l - poolingPadding;
95 | if (
96 | row >= 0 &&
97 | row < inputRowSize &&
98 | col >= 0 &&
99 | col < inputColSize
100 | ) {
101 | max = Math.max(max, inputData[b][n][row][col]);
102 | }
103 | }
104 | }
105 | rowData.push(max);
106 | }
107 | filterData.push(rowData);
108 | }
109 | batchData.push(filterData);
110 | }
111 | poolingData.push(batchData);
112 | }
113 |
114 | return poolingData;
115 | };
116 |
117 | export function reshapeArray(array: number[][][][]) {
118 | const reshapeArray = [];
119 | const batchSize = array.length;
120 | const channelSize = array[0].length;
121 | const rowSize = array[0][0].length;
122 | const colSize = array[0][0][0].length;
123 |
124 | for (let b = 0; b < batchSize; b++) {
125 | const reshapeRowData = [];
126 | for (let c = 0; c < channelSize; c++) {
127 | for (let r = 0; r < rowSize; r++) {
128 | for (let col = 0; col < colSize; col++) {
129 | reshapeRowData.push(array[b][c][r][col]);
130 | }
131 | }
132 | }
133 | reshapeArray.push(reshapeRowData);
134 | }
135 |
136 | return reshapeArray;
137 | }
138 |
139 | export function reshapeImageArray(array: number[][][][]) {
140 | const reshapeArray = [];
141 | const batchSize = array.length;
142 | const channelSize = array[0].length;
143 | const rowSize = array[0][0].length;
144 | const colSize = array[0][0][0].length;
145 |
146 | for (let b = 0; b < batchSize; b++) {
147 | const reshapeRowData = [];
148 | for (let c = 0; c < channelSize; c++) {
149 | const reshapeData = [];
150 | for (let r = 0; r < rowSize; r++) {
151 | for (let col = 0; col < colSize; col++) {
152 | reshapeData.push(array[b][c][r][col]);
153 | }
154 | }
155 | reshapeRowData.push(reshapeData);
156 | }
157 | reshapeArray.push(reshapeRowData);
158 | }
159 |
160 | return reshapeArray;
161 | }
162 |
163 | export const reshapeArray3to1 = (array: number[][][]) => {
164 | const reshapeArray = [];
165 | const batchSize = array.length;
166 | const rowSize = array[0].length;
167 | const colSize = array[0][0].length;
168 |
169 | for (let b = 0; b < batchSize; b++) {
170 | const reshapeRowData = [];
171 | for (let r = 0; r < rowSize; r++) {
172 | for (let col = 0; col < colSize; col++) {
173 | reshapeRowData.push(array[b][r][col]);
174 | }
175 | }
176 | reshapeArray.push(reshapeRowData);
177 | }
178 |
179 | return reshapeArray;
180 | };
181 |
182 | // アフィン変換の計算を行う
183 | export const calcAffine = (
184 | input: number[][],
185 | weight: number[][],
186 | bias: number[],
187 | ) => {
188 | // ここにアフィン変換の計算を書く
189 | const output = [];
190 | for (let i = 0; i < input.length; i++) {
191 | const row = [];
192 | for (let j = 0; j < weight[0].length; j++) {
193 | let sum = 0;
194 | for (let k = 0; k < input[0].length; k++) {
195 | sum += input[i][k] * weight[k][j];
196 | }
197 | row.push(sum + bias[j]);
198 | }
199 | output.push(row);
200 | }
201 |
202 | return output;
203 | };
204 |
205 | export const calcRelu2DArray = (input: number[][]) => {
206 | // ここにReLU関数の計算を書く
207 | const output = [];
208 | for (let i = 0; i < input.length; i++) {
209 | const row = [];
210 | for (let j = 0; j < input[0].length; j++) {
211 | row.push(Math.max(0, input[i][j]));
212 | }
213 | output.push(row);
214 | }
215 | return output;
216 | };
217 |
218 | export const calcRelu4DArray = (input: number[][][][]) => {
219 | // ここにReLU関数の計算を書く
220 | const output = [];
221 | for (let b = 0; b < input.length; b++) {
222 | const batchData = [];
223 | for (let n = 0; n < input[0].length; n++) {
224 | const filterData = [];
225 | for (let i = 0; i < input[0][0].length; i++) {
226 | const rowData = [];
227 | for (let j = 0; j < input[0][0][0].length; j++) {
228 | rowData.push(Math.max(0, input[b][n][i][j]));
229 | }
230 | filterData.push(rowData);
231 | }
232 | batchData.push(filterData);
233 | }
234 | output.push(batchData);
235 | }
236 | return output;
237 | };
238 |
239 | // softmax関数の計算を行う
240 | export const calcSoftMax = (input: number[][]) => {
241 | const output = [];
242 | for (let i = 0; i < input.length; i++) {
243 | let sum = 0;
244 | const row = [];
245 | // rowの最大値を求める
246 | const maxVal = Math.max(...input[i]);
247 | for (let j = 0; j < input[0].length; j++) {
248 | sum += Math.exp(input[i][j] - maxVal);
249 | }
250 | for (let j = 0; j < input[0].length; j++) {
251 | row.push(Math.exp(input[i][j] - maxVal) / sum);
252 | }
253 | output.push(row);
254 | }
255 | return output;
256 | };
257 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | // 数字をスライドすることができる
2 | import { useEffect, useMemo, useState, useRef, useCallback } from 'react';
3 | import './App.css';
4 | import { Canvas } from '@react-three/fiber';
5 | import { OrbitControls } from '@react-three/drei';
6 | import { Vector3 } from 'three';
7 | import { ministSampleData } from './components/visual/mnist-sample';
8 | import { useWindowSize } from './components/visual/use-window-size';
9 | import { InputHiddenPlane } from './components/visual/input-hidden-plane';
10 | import {
11 | useCovolutionMemo,
12 | usePoolingMemo,
13 | } from './components/neuralnet/covolution';
14 | import {
15 | calcAffine,
16 | calcRelu2DArray,
17 | reshapeArray,
18 | } from './components/neuralnet/functions';
19 | import Sidebar from './components/ui/sidebar';
20 | import { ResultMesh } from './components/visual/result-mesh';
21 | import { DrawLineGroup } from './components/visual/drawLineGroup';
22 |
23 | function App() {
24 | const autoNumCntRef = useRef(
25 | Math.floor(Math.random() * ministSampleData.length),
26 | );
27 | const isButtonClicked = useRef(false);
28 |
29 | const [inputData, setInputData] = useState(
30 | ministSampleData[autoNumCntRef.current],
31 | );
32 | const windowSize = useWindowSize();
33 |
34 | const inputSize = 28 * 28;
35 | const inputRowSize = 28;
36 | const inputColSize = 28;
37 |
38 | // covolutionのパラメータ
39 | const filterNum = 30;
40 | const filterRowSize = 5;
41 | const filterColSize = 5;
42 | const covolutionStride = 1;
43 | const covolutionPadding = 0;
44 |
45 | const inputPlane = { size: 1 / 10, space: 0.02 }; // const size = 1 / 14 const space = 0.02;
46 | const covolutionPlane = {
47 | size: 1.2,
48 | space: 0.1,
49 | divide: 24,
50 | num: 30,
51 | row: 5,
52 | col: 6,
53 | };
54 | const poolingPlane = {
55 | size: 1.8,
56 | space: 0.2,
57 | divide: 12,
58 | num: 30,
59 | row: 6,
60 | col: 5,
61 | };
62 | const hiddenPlane = {
63 | size: 0.4,
64 | space: 0.15,
65 | num: 100,
66 | row: 10,
67 | col: 10,
68 | };
69 | const outputPlane = {
70 | size: 0.3,
71 | space: 0.1,
72 | num: 10,
73 | row: 2,
74 | col: 5,
75 | };
76 |
77 | const inputPos = new Vector3(0, 0, 0);
78 | const covolutionPos = new Vector3(0, 0, -0.5);
79 | const poolingPos = new Vector3(0, 0, -0.5);
80 | const hiddenPos = new Vector3(0, 0, -8);
81 | const outputPos = new Vector3(0, 0, -16);
82 |
83 | const [params, setParams] = useState<{
84 | W1: number[][][][];
85 | b1: number[];
86 | W2: number[][];
87 | b2: number[];
88 | W3: number[][];
89 | b3: number[];
90 | } | null>(null);
91 |
92 | // useCovolution + Relu
93 | const covolutionData = useCovolutionMemo(
94 | [[inputData]],
95 | params?.W1,
96 | params?.b1,
97 | inputRowSize,
98 | inputColSize,
99 | filterNum,
100 | filterRowSize,
101 | filterColSize,
102 | covolutionPadding,
103 | covolutionStride,
104 | );
105 | const poolingData = usePoolingMemo(covolutionData, 2, 2, 2, 0);
106 | const hiddenData = useMemo(() => {
107 | if (!poolingData || !params?.W2 || !params?.b2) return undefined;
108 | const res = reshapeArray(poolingData);
109 | return calcAffine(res, params?.W2, params?.b2);
110 | }, [poolingData, params?.W2, params?.b2]);
111 | const outputData = useMemo(() => {
112 | if (!hiddenData || !params?.W3 || !params?.b3) return undefined;
113 | const res = calcRelu2DArray(hiddenData);
114 | return calcAffine(res, params?.W3, params?.b3);
115 | }, [hiddenData, params?.W3, params?.b3]);
116 |
117 | // result用に使用するデータ
118 | // resultMeshに渡すために、softmax関数をかけた結果の配列を作成する。
119 | const outputSoftmaxArr = useMemo(() => {
120 | if (outputData === undefined) {
121 | return undefined;
122 | }
123 | const result = outputData[0];
124 | const max = Math.max(...result);
125 | const exp = result.map((x) => Math.exp(x - max));
126 | const sumExp = exp.reduce((a, b) => a + b);
127 | return exp.map((x) => x / sumExp);
128 | }, [outputData]);
129 |
130 | // poolingDataをsigmoidに変換
131 | // useMemoでキャッシュ
132 | const poolingDataSigmoid = useMemo(() => {
133 | if (!poolingData) return undefined;
134 |
135 | const sigmoid = (x: number) => 1 / (1 + Math.exp(-x));
136 |
137 | const result = poolingData.map((data) =>
138 | data.map((data) =>
139 | data.map((data) => data.map((data) => sigmoid(data) * 2 - 1)),
140 | ),
141 | );
142 |
143 | return result;
144 | }, [poolingData]);
145 |
146 | // hiddenDataをsigmoidに変換
147 | // useMemoでキャッシュ
148 | const hiddenDataSigmoid = useMemo(() => {
149 | if (!hiddenData) return undefined;
150 |
151 | const sigmoid = (x: number) => 1 / (1 + Math.exp(-x));
152 |
153 | const result = hiddenData.map((data) => data.map((data) => sigmoid(data)));
154 |
155 | return result;
156 | }, [hiddenData]);
157 |
158 | const isLoading = useMemo(() => {
159 | return params === null;
160 | }, [params]);
161 | const displayLoading = useMemo(() => {
162 | return isLoading ? 'block' : 'hidden';
163 | }, [isLoading]);
164 | const displayCanvas = useMemo(() => {
165 | return isLoading ? 'hidden' : 'block';
166 | }, [isLoading]);
167 | const displaySideBar = useMemo(() => {
168 | return isLoading ? 'hidden' : 'flex';
169 | }, [isLoading]);
170 |
171 | useEffect(() => {
172 | fetch('/cnn-params.json')
173 | .then((res) => res.json())
174 | .then((data) => {
175 | setParams(data);
176 | });
177 | }, []);
178 |
179 | const clickButton = useCallback(() => {
180 | autoNumCntRef.current = Math.floor(Math.random() * ministSampleData.length);
181 | isButtonClicked.current = true;
182 | setInputData(ministSampleData[autoNumCntRef.current]);
183 | }, []);
184 |
185 | return (
186 | <>
187 |
191 |
192 |
234 |
235 | {/* 画面の中央にLOADINGを表示する */}
236 |
243 |
244 |
245 |
253 | >
254 | );
255 | }
256 |
257 | export default App;
258 |
--------------------------------------------------------------------------------
/src/components/visual/drawLineGroup.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo, useRef } from 'react';
2 | import { BufferGeometry, MathUtils, Vector3 } from 'three';
3 |
4 | type DrawLineGroupProps = {
5 | inputData: number[];
6 | inputPlane: {
7 | size: number;
8 | space: number;
9 | };
10 | inputPos: Vector3;
11 | inputSize: number;
12 | inputRowSize: number;
13 | inputColSize: number;
14 | poolingData: number[][][][] | undefined;
15 | poolingPlane: {
16 | size: number;
17 | space: number;
18 | divide: number;
19 | num: number;
20 | row: number;
21 | col: number;
22 | };
23 | poolingPos: Vector3;
24 | hiddenData: number[][] | undefined;
25 | hiddenPlane: {
26 | size: number;
27 | space: number;
28 | num: number;
29 | row: number;
30 | col: number;
31 | };
32 | hiddenPos: Vector3;
33 | resultData: number[] | undefined;
34 | resultPlane: {
35 | size: number;
36 | space: number;
37 | num: number;
38 | row: number;
39 | col: number;
40 | };
41 | resultPos: Vector3;
42 | };
43 |
44 | export function DrawLineGroup(props: DrawLineGroupProps) {
45 | const {
46 | inputData,
47 | inputPlane,
48 | inputPos,
49 | inputRowSize,
50 | inputColSize,
51 | poolingData,
52 | poolingPlane,
53 | poolingPos,
54 | hiddenData,
55 | hiddenPlane,
56 | hiddenPos,
57 | resultData,
58 | resultPlane,
59 | resultPos,
60 | } = props;
61 | const geometry = useRef(null!);
62 | const poolingNumPerUnit = 60;
63 | const inputNum = 5000;
64 |
65 | // input -> poolingの線を引く。
66 | // inputNum個のobjectを作成し、arrayに入れる。
67 | // objectは inputRow 0 - inputRowSize - 1, inputCol 0 - inputColSize の乱数と
68 | // divide: {
69 | // row: MathUtils.randInt(0, poolingPlane.divide - 1),
70 | // col: MathUtils.randInt(0, poolingPlane.divide - 1),
71 | // },
72 | // main: {
73 | // row: MathUtils.randInt(0, poolingPlane.row - 1),
74 | // col: MathUtils.randInt(0, poolingPlane.col - 1),
75 | // },
76 | // の乱数の2つを持つ。
77 | // useMemoを使って、キャッシュする。
78 | const inputPairArr = useMemo(() => {
79 | const arr = [];
80 | for (let i = 0; i < inputNum; i++) {
81 | arr.push({
82 | input: {
83 | row: MathUtils.randInt(0, inputRowSize - 1),
84 | col: MathUtils.randInt(0, inputColSize - 1),
85 | },
86 | pooling: {
87 | divide: {
88 | row: MathUtils.randInt(0, poolingPlane.divide - 1),
89 | col: MathUtils.randInt(0, poolingPlane.divide - 1),
90 | },
91 | main: {
92 | row: MathUtils.randInt(0, poolingPlane.row - 1),
93 | col: MathUtils.randInt(0, poolingPlane.col - 1),
94 | },
95 | },
96 | });
97 | }
98 | return arr;
99 | }, []);
100 |
101 | // 0 - hiddenPlane.num - 1 に対して それおぞれ poolingNumPerUnit 個のobjectを作成する。
102 | // objectは 0 - poolingPlane.num - 1 の乱数、 0 - poolingPlane.divide - 1 の乱数、 0 - 1 の乱数を2つ持つ。
103 | // useMemoを使って、キャッシュする。
104 | const poolingInputPairArr = useMemo(() => {
105 | const arr = [];
106 | // for (let i = 0; i < poolingHiddenNumber; i++) {
107 | // arr.push(MathUtils.(0, 1));
108 | // }
109 | for (let i = 0; i < hiddenPlane.num; i++) {
110 | // arr.push(MathUtils.randFloat(0, 1));
111 | const rowArr = [];
112 | for (let j = 0; j < poolingNumPerUnit; j++) {
113 | // rowArr.push(MathUtils.randFloat(0, 1));
114 | rowArr.push({
115 | divide: {
116 | row: MathUtils.randInt(0, poolingPlane.divide - 1),
117 | col: MathUtils.randInt(0, poolingPlane.divide - 1),
118 | },
119 | main: {
120 | row: MathUtils.randInt(0, poolingPlane.row - 1),
121 | col: MathUtils.randInt(0, poolingPlane.col - 1),
122 | },
123 | });
124 | }
125 | arr.push(rowArr);
126 | }
127 |
128 | return arr;
129 | }, []);
130 |
131 | const pos = useMemo(() => {
132 | const arr = [];
133 |
134 | // input -> poolingの線を引く。
135 | for (let i = 0; i < inputNum; i++) {
136 | const inputObj = inputPairArr[i];
137 | const inputPosX =
138 | inputPos.x +
139 | (inputObj.input.col - (inputColSize - 1) / 2) *
140 | (inputPlane.size + inputPlane.space);
141 | const inputPosY =
142 | inputPos.y -
143 | (inputObj.input.row - (inputRowSize - 1) / 2) *
144 | (inputPlane.size + inputPlane.space);
145 | const inputPosZ = inputPos.z;
146 | const poolingObj = inputObj.pooling;
147 | const poolGridSize = poolingPlane.size / poolingPlane.divide;
148 | const poolingPosX =
149 | poolingPos.x +
150 | (poolingObj.main.col - (poolingPlane.col - 1) / 2) *
151 | (poolingPlane.size + poolingPlane.space) +
152 | (poolingObj.divide.col - (poolingPlane.divide - 1) / 2) * poolGridSize +
153 | poolGridSize * MathUtils.randFloat(-0.5, 0.5);
154 |
155 | const poolingPosY =
156 | poolingPos.y -
157 | (poolingObj.main.row - (poolingPlane.row - 1) / 2) *
158 | (poolingPlane.size + poolingPlane.space) -
159 | (poolingObj.divide.row - (poolingPlane.divide - 1) / 2) * poolGridSize +
160 | poolGridSize * MathUtils.randFloat(-0.5, 0.5);
161 |
162 | const poolingPosZ = poolingPos.z;
163 | arr.push(
164 | inputPosX + inputPlane.size * MathUtils.randFloat(-0.5, 0.5),
165 | inputPosY + inputPlane.size * MathUtils.randFloat(-0.5, 0.5),
166 | inputPosZ,
167 | poolingPosX,
168 | poolingPosY,
169 | poolingPosZ,
170 | );
171 | }
172 |
173 | // pooling -> hiddenの線を引く。
174 | for (let hiddenRow = 0; hiddenRow < hiddenPlane.row; hiddenRow++) {
175 | for (let hiddenCol = 0; hiddenCol < hiddenPlane.col; hiddenCol++) {
176 | const hiddenIndex = hiddenRow * hiddenPlane.row + hiddenCol;
177 | const hiddenPosX =
178 | hiddenPos.x +
179 | (hiddenCol - (hiddenPlane.col - 1) / 2) *
180 | (hiddenPlane.size + hiddenPlane.space);
181 | const hiddenPosY =
182 | hiddenPos.y +
183 | (hiddenRow - (hiddenPlane.row - 1) / 2) *
184 | (hiddenPlane.size + hiddenPlane.space);
185 | const hiddenPosZ = hiddenPos.z;
186 | for (
187 | let poolingIndex = 0;
188 | poolingIndex < poolingNumPerUnit;
189 | poolingIndex++
190 | ) {
191 | const poolingObj = poolingInputPairArr[hiddenIndex][poolingIndex];
192 | const poolGridSize = poolingPlane.size / poolingPlane.divide;
193 | const poolingPosX =
194 | poolingPos.x +
195 | (poolingObj.main.col - (poolingPlane.col - 1) / 2) *
196 | (poolingPlane.size + poolingPlane.space) +
197 | (poolingObj.divide.col - (poolingPlane.divide - 1) / 2) *
198 | poolGridSize +
199 | poolGridSize * MathUtils.randFloat(-0.5, 0.5);
200 |
201 | const poolingPosY =
202 | poolingPos.y -
203 | (poolingObj.main.row - (poolingPlane.row - 1) / 2) *
204 | (poolingPlane.size + poolingPlane.space) -
205 | (poolingObj.divide.row - (poolingPlane.divide - 1) / 2) *
206 | poolGridSize +
207 | poolGridSize * MathUtils.randFloat(-0.5, 0.5);
208 |
209 | const poolingPosZ = poolingPos.z;
210 | arr.push(
211 | hiddenPosX + hiddenPlane.size * MathUtils.randFloat(-0.5, 0.5),
212 | hiddenPosY + hiddenPlane.size * MathUtils.randFloat(-0.5, 0.5),
213 | hiddenPosZ,
214 | poolingPosX,
215 | poolingPosY,
216 | poolingPosZ,
217 | );
218 | }
219 | }
220 | }
221 |
222 | // hidden -> outputの線を引く。
223 | for (let outputIndex = 0; outputIndex < resultPlane.num; outputIndex++) {
224 | const theta = (outputIndex * Math.PI) / 5 - Math.PI / 2;
225 | const rad = 8;
226 | const x = Math.cos(theta) * rad;
227 | const y = -Math.sin(theta) * rad;
228 | const outputPosX = resultPos.x + x;
229 | const outputPosY = resultPos.y + y;
230 | const outputPosZ = resultPos.z;
231 | for (let hiddenIndex = 0; hiddenIndex < hiddenPlane.num; hiddenIndex++) {
232 | const hiddenRow = Math.floor(hiddenIndex / hiddenPlane.row);
233 | const hiddenCol = hiddenIndex % hiddenPlane.row;
234 | const hiddenPosX =
235 | hiddenPos.x +
236 | (hiddenCol - (hiddenPlane.col - 1) / 2) *
237 | (hiddenPlane.size + hiddenPlane.space) +
238 | hiddenPlane.size * MathUtils.randFloat(-0.5, 0.5);
239 | const hiddenPosY =
240 | hiddenPos.y -
241 | (hiddenRow - (hiddenPlane.row - 1) / 2) *
242 | (hiddenPlane.size + hiddenPlane.space) +
243 | hiddenPlane.size * MathUtils.randFloat(-0.5, 0.5);
244 | const hiddenPosZ = hiddenPos.z + 0.1;
245 | arr.push(
246 | hiddenPosX,
247 | hiddenPosY,
248 | hiddenPosZ,
249 | outputPosX + 0.1 * MathUtils.randFloat(-1, 1),
250 | outputPosY + 0.1 * MathUtils.randFloat(-1, 1),
251 | outputPosZ,
252 | );
253 | }
254 | }
255 |
256 | if (geometry.current) {
257 | geometry.current.attributes.position.needsUpdate = true;
258 | }
259 | return new Float32Array(arr);
260 | }, []);
261 |
262 | const color = useMemo(() => {
263 | const colors = [];
264 | // input -> poolingのvalueを色として配列に入れる。
265 | for (let i = 0; i < inputNum; i++) {
266 | const inputObj = inputPairArr[i];
267 | const inputIndex = inputObj.input.row * inputRowSize + inputObj.input.col;
268 | const inputValue = inputData ? inputData[inputIndex] : 0;
269 | const inputColorVal = inputValue;
270 | const inputAlphaVal = Math.max(inputValue * 0.5, 0.01);
271 | const poolingObj = inputObj.pooling;
272 | const poolingIndex =
273 | inputObj.pooling.main.row * poolingPlane.col +
274 | inputObj.pooling.main.col;
275 |
276 | const poolingColorVal = poolingData
277 | ? poolingData[0][poolingIndex][poolingObj.divide.row][
278 | poolingObj.divide.col
279 | ]
280 | : 0;
281 | const poolingAlphaVal = Math.max(poolingColorVal * 0.4, 0.01);
282 | colors.push(
283 | inputColorVal,
284 | inputColorVal,
285 | inputColorVal,
286 | inputAlphaVal,
287 | poolingColorVal,
288 | poolingColorVal,
289 | poolingColorVal,
290 | poolingAlphaVal,
291 | );
292 | }
293 |
294 | // pooling -> hiddenのvalueを色として配列に入れる。
295 | for (let hiddenRow = 0; hiddenRow < hiddenPlane.row; hiddenRow++) {
296 | for (let hiddenCol = 0; hiddenCol < hiddenPlane.col; hiddenCol++) {
297 | const hiddenIndex = hiddenRow * hiddenPlane.row + hiddenCol;
298 | const hiddenValue = hiddenData ? hiddenData[0][hiddenIndex] : 0;
299 | const hiddenColorVal = hiddenValue;
300 | const hiddenAlphaVal = hiddenValue * 0.1;
301 | for (
302 | let poolingIndex = 0;
303 | poolingIndex < poolingNumPerUnit;
304 | poolingIndex++
305 | ) {
306 | const poolObj = poolingInputPairArr[hiddenIndex][poolingIndex];
307 | // const poolObj2 = poolObj[poolingIndex];
308 | const index = poolObj.main.row * poolingPlane.col + poolObj.main.col;
309 | const poolingColorVal = poolingData
310 | ? poolingData[0][index][poolObj.divide.row][poolObj.divide.col]
311 | : 0;
312 | const poolingAlphaVal = poolingColorVal * 0.1;
313 | colors.push(
314 | hiddenColorVal,
315 | hiddenColorVal,
316 | hiddenColorVal,
317 | hiddenAlphaVal,
318 | poolingColorVal,
319 | poolingColorVal,
320 | poolingColorVal,
321 | poolingAlphaVal,
322 | );
323 | }
324 | }
325 | }
326 |
327 | // hidden -> outputのvalueを色として配列に入れる。
328 | for (let outputIndex = 0; outputIndex < resultPlane.num; outputIndex++) {
329 | const outputValue = resultData ? resultData[outputIndex] : 0;
330 | const outputColorVal = outputValue;
331 | const outputAlphaVal = outputValue * 0.1;
332 | for (let hiddenIndex = 0; hiddenIndex < hiddenPlane.num; hiddenIndex++) {
333 | const hiddenColorVal = hiddenData ? hiddenData[0][hiddenIndex] : 0;
334 | const hiddenAlphaVal = hiddenColorVal * 0.1;
335 | colors.push(
336 | hiddenColorVal,
337 | hiddenColorVal,
338 | hiddenColorVal,
339 | hiddenAlphaVal,
340 | outputColorVal,
341 | outputColorVal,
342 | outputColorVal,
343 | outputAlphaVal,
344 | );
345 | }
346 | }
347 |
348 | if (geometry.current) {
349 | geometry.current.attributes.color.needsUpdate = true;
350 | }
351 | return new Float32Array(colors);
352 | }, [inputData, poolingData, hiddenData, resultData]);
353 |
354 | return (
355 |
356 |
357 |
363 |
369 |
370 |
378 |
379 | );
380 | }
381 |
--------------------------------------------------------------------------------
/src/components/visual/input-hidden-plane.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useMemo, useRef } from 'react';
2 | import { BufferAttribute, DoubleSide, Vector3 } from 'three';
3 |
4 | type InputPlanePlaneProps = JSX.IntrinsicElements['group'] & {
5 | inputData: number[];
6 | inputSize: number;
7 | inputRowSize: number;
8 | inputColSize: number;
9 | inputPlane: { size: number; space: number };
10 | inputPos: Vector3;
11 | covolutionData: number[][][][] | undefined;
12 | covolutionPlane: {
13 | size: number;
14 | space: number;
15 | divide: number;
16 | num: number;
17 | row: number;
18 | col: number;
19 | };
20 | covolutionPos: Vector3;
21 | poolingData: number[][][][] | undefined;
22 | poolingPlane: {
23 | size: number;
24 | space: number;
25 | divide: number;
26 | num: number;
27 | row: number;
28 | col: number;
29 | };
30 | poolingPos: Vector3;
31 | hiddenData: number[][] | undefined;
32 | hiddenPlane: {
33 | size: number;
34 | space: number;
35 | num: number;
36 | row: number;
37 | col: number;
38 | };
39 | hiddenPos: Vector3;
40 | };
41 |
42 | export function InputHiddenPlane(props: InputPlanePlaneProps) {
43 | const inputColorBufferAttribute = useRef(null);
44 | const poolingColorBufferAttribute = useRef(null);
45 | const hiddenColorBufferAttribute = useRef(null);
46 |
47 | const getInputGeometryArrayResult = useMemo(
48 | () =>
49 | getInputGeometryArray(
50 | props.inputData,
51 | props.inputSize,
52 | props.inputRowSize,
53 | props.inputColSize,
54 | props.inputPlane,
55 | props.inputPos,
56 | ),
57 | [],
58 | );
59 |
60 | useEffect(() => {
61 | if (inputColorBufferAttribute.current) {
62 | // update color
63 | const colorArray = inputColorBufferAttribute.current
64 | .array as Float32Array;
65 | for (let i = 0; i < props.inputData.length; i++) {
66 | const colorArrayIndex = i * 4 * 3;
67 | for (let j = 0; j < 4 * 3; j++) {
68 | colorArray[colorArrayIndex + j] = props.inputData[i];
69 | }
70 | }
71 |
72 | inputColorBufferAttribute.current.needsUpdate = true;
73 | }
74 | }, [props.inputData]);
75 |
76 | // poolingDataSigmoidを使ってgetPoolingGeometryArrayを呼び出す
77 | const getPoolingGeometryArrayResult = useMemo(
78 | () =>
79 | getPoolingGeometryArray(
80 | props.poolingData,
81 | props.poolingPlane,
82 | props.poolingPos,
83 | ),
84 | [],
85 | );
86 |
87 | // useEffectでpoolingColorBufferAttributeを更新
88 | useEffect(() => {
89 | if (poolingColorBufferAttribute.current && props.poolingData) {
90 | // update color
91 | const colorArray = poolingColorBufferAttribute.current
92 | .array as Float32Array;
93 |
94 | const channel = props.poolingData[0].length;
95 | const row = props.poolingData[0][0].length;
96 | const col = props.poolingData[0][0][0].length;
97 | for (let c = 0; c < channel; c++) {
98 | for (let r = 0; r < row; r++) {
99 | for (let l = 0; l < col; l++) {
100 | const colorArrayIndex =
101 | c * row * col * 4 * 3 + r * col * 4 * 3 + l * 4 * 3;
102 | for (let i = 0; i < 4 * 3; i++) {
103 | colorArray[colorArrayIndex + i] = props.poolingData[0][c][r][l];
104 | }
105 | }
106 | }
107 | }
108 |
109 | poolingColorBufferAttribute.current.needsUpdate = true;
110 | }
111 | }, [props.poolingData]);
112 |
113 | // hiddenDataSigmoidを使ってgetHiddenGeometryArrayを呼び出す
114 | const getHiddenGeometryArrayResult = useMemo(
115 | () =>
116 | getInputGeometryArray(
117 | props.hiddenData ? props.hiddenData[0] : undefined,
118 | props.hiddenPlane.num,
119 | props.hiddenPlane.row,
120 | props.hiddenPlane.col,
121 | props.hiddenPlane,
122 | props.hiddenPos,
123 | ),
124 | [],
125 | );
126 |
127 | useEffect(() => {
128 | if (
129 | hiddenColorBufferAttribute.current &&
130 | props.hiddenData &&
131 | props.hiddenData[0]
132 | ) {
133 | // update color
134 | const colorArray = hiddenColorBufferAttribute.current
135 | .array as Float32Array;
136 | for (let i = 0; i < props.hiddenData[0].length; i++) {
137 | const colorArrayIndex = i * 4 * 3;
138 | for (let j = 0; j < 4 * 3; j++) {
139 | colorArray[colorArrayIndex + j] = props.hiddenData[0][i]
140 | ? props.hiddenData[0][i]
141 | : 0;
142 | }
143 | }
144 |
145 | hiddenColorBufferAttribute.current.needsUpdate = true;
146 | }
147 | }, [props.hiddenData]);
148 |
149 | // line segments用のdataを作成
150 | //
151 |
152 | const mat1 = useMemo(
153 | () => (
154 |
175 | ),
176 | [],
177 | );
178 |
179 | const mat2 = useMemo(
180 | () => (
181 |
202 | ),
203 | [],
204 | );
205 |
206 | return (
207 |
208 |
209 |
210 |
216 |
223 |
229 |
230 | {mat1}
231 |
232 |
233 |
234 |
240 |
247 |
253 |
254 | {mat1}
255 |
256 |
257 |
258 |
264 |
271 |
277 |
278 | {mat2}
279 |
280 |
281 | );
282 | }
283 |
284 | function getInputGeometryArray(
285 | inputData: number[] | undefined,
286 | inputSize: number,
287 | inputRowSize: number,
288 | inputColSize: number,
289 | inputPlane: { size: number; space: number },
290 | inputPos: Vector3,
291 | ): {
292 | position: Float32Array;
293 | color: Float32Array;
294 | index: Uint16Array;
295 | } {
296 | const inputGapSize = inputPlane.size + inputPlane.space;
297 | const inputHalfSideSize = inputPlane.size / 2;
298 |
299 | const position = new Float32Array(inputSize * 4 * 3);
300 | const color = new Float32Array(inputSize * 4 * 3);
301 | const index = new Uint16Array(inputSize * 6);
302 |
303 | let positionIndex = 0;
304 | let colorIndex = 0;
305 | let indexIndex = 0;
306 |
307 | const startX = inputPos.x - (inputGapSize * (inputRowSize - 1)) / 2;
308 | const startY = inputPos.y + (inputGapSize * (inputColSize - 1)) / 2;
309 | const startZ = inputPos.z;
310 |
311 | for (let col = 0; col < inputColSize; col++) {
312 | for (let row = 0; row < inputRowSize; row++) {
313 | const x = startX + row * inputGapSize;
314 | const y = startY - col * inputGapSize;
315 | const z = startZ;
316 |
317 | const colorValue =
318 | inputData && inputData[inputRowSize + col * inputColSize + row]
319 | ? inputData[inputRowSize + col * inputColSize + row]
320 | : 0;
321 |
322 | position[positionIndex] = x - inputHalfSideSize;
323 | position[positionIndex + 1] = y - inputHalfSideSize;
324 | position[positionIndex + 2] = z;
325 | position[positionIndex + 3] = x - inputHalfSideSize;
326 | position[positionIndex + 4] = y + inputHalfSideSize;
327 | position[positionIndex + 5] = z;
328 | position[positionIndex + 6] = x + inputHalfSideSize;
329 | position[positionIndex + 7] = y + inputHalfSideSize;
330 | position[positionIndex + 8] = z;
331 | position[positionIndex + 9] = x + inputHalfSideSize;
332 | position[positionIndex + 10] = y - inputHalfSideSize;
333 | position[positionIndex + 11] = z;
334 |
335 | for (let i = 0; i < 4; i++) {
336 | color[colorIndex + 3 * i] = colorValue;
337 | color[colorIndex + 3 * i + 1] = colorValue;
338 | color[colorIndex + 3 * i + 2] = colorValue;
339 | }
340 |
341 | const indexValue = 4 * (row + col * inputRowSize);
342 | index[indexIndex] = indexValue;
343 | index[indexIndex + 1] = indexValue + 1;
344 | index[indexIndex + 2] = indexValue + 2;
345 | index[indexIndex + 3] = indexValue + 2;
346 | index[indexIndex + 4] = indexValue + 3;
347 | index[indexIndex + 5] = indexValue;
348 |
349 | positionIndex += 12;
350 | colorIndex += 12;
351 | indexIndex += 6;
352 | }
353 | }
354 |
355 | return { position, color, index };
356 | }
357 |
358 | function getPoolingGeometryArray(
359 | poolingDataSigmoid: number[][][][] | undefined,
360 | poolingPlane: {
361 | size: number;
362 | space: number;
363 | divide: number;
364 | num: number;
365 | row: number;
366 | col: number;
367 | },
368 | poolingPos: Vector3,
369 | ) {
370 | // Your code here
371 | // poolingDataSigmoidがundefinedの場合はreturn undefined
372 | // if (!poolingDataSigmoid) return undefined;
373 |
374 | // position, color, indexの配列を作成
375 | const poolingSize = poolingPlane.size;
376 | const divide = poolingPlane.divide;
377 | const gridSize = poolingSize / poolingPlane.divide;
378 | const space = poolingPlane.space;
379 | const planeNum = poolingPlane.num;
380 | const row = poolingPlane.row;
381 | const col = poolingPlane.col;
382 |
383 | // position, color, indexの配列を作成
384 | const position = new Float32Array(planeNum * divide * divide * 4 * 3);
385 | const color = new Float32Array(planeNum * divide * divide * 4 * 3);
386 | const index = new Uint16Array(planeNum * divide * divide * 6);
387 |
388 | let positionIndex = 0;
389 | let colorIndex = 0;
390 | let indexIndex = 0;
391 | // for文を使ってposition, color, indexの配列を作成
392 | // for row -> for col -> for gridY -> for gridX
393 | for (let r = 0; r < row; r++) {
394 | for (let c = 0; c < col; c++) {
395 | for (let gridY = 0; gridY < divide; gridY++) {
396 | for (let gridX = 0; gridX < divide; gridX++) {
397 | // Your code here
398 | const x =
399 | poolingPos.x +
400 | (poolingSize + space) * c +
401 | gridSize * gridX -
402 | ((poolingSize + space) * (col - 1)) / 2 -
403 | (gridSize * (divide - 1)) / 2;
404 |
405 | const y =
406 | poolingPos.y -
407 | (poolingSize + space) * r -
408 | gridSize * gridY +
409 | ((poolingSize + space) * (row - 1)) / 2 +
410 | (gridSize * (divide - 1)) / 2;
411 |
412 | const z = poolingPos.z;
413 |
414 | // poolingDataSigmoidからcolorを取得 (poolingDataSigmoid[0][filterNum][gridY][gridX])
415 | const colorValue = poolingDataSigmoid
416 | ? poolingDataSigmoid[0][r * col + c][gridY][gridX]
417 | : 0;
418 |
419 | // indexの値を計算
420 | const indexValue =
421 | 4 *
422 | (r * col * divide * divide +
423 | c * divide * divide +
424 | gridY * divide +
425 | gridX);
426 |
427 | // position, color, indexの配列に値を代入
428 | const rate = 0.7;
429 | position[positionIndex] = x - (gridSize / 2) * rate;
430 | position[positionIndex + 1] = y - (gridSize / 2) * rate;
431 | position[positionIndex + 2] = z;
432 | position[positionIndex + 3] = x - (gridSize / 2) * rate;
433 | position[positionIndex + 4] = y + (gridSize / 2) * rate;
434 | position[positionIndex + 5] = z;
435 | position[positionIndex + 6] = x + (gridSize / 2) * rate;
436 | position[positionIndex + 7] = y + (gridSize / 2) * rate;
437 | position[positionIndex + 8] = z;
438 | position[positionIndex + 9] = x + (gridSize / 2) * rate;
439 | position[positionIndex + 10] = y - (gridSize / 2) * rate;
440 | position[positionIndex + 11] = z;
441 |
442 | for (let i = 0; i < 4; i++) {
443 | color[colorIndex + 3 * i] = colorValue;
444 | color[colorIndex + 3 * i + 1] = colorValue;
445 | color[colorIndex + 3 * i + 2] = colorValue;
446 | }
447 |
448 | index[indexIndex] = indexValue;
449 | index[indexIndex + 1] = indexValue + 1;
450 | index[indexIndex + 2] = indexValue + 2;
451 | index[indexIndex + 3] = indexValue + 2;
452 | index[indexIndex + 4] = indexValue + 3;
453 | index[indexIndex + 5] = indexValue;
454 |
455 | positionIndex += 12;
456 | colorIndex += 12;
457 | indexIndex += 6;
458 | }
459 | }
460 | }
461 | }
462 |
463 | return { position, color, index };
464 | }
465 |
--------------------------------------------------------------------------------
/src/components/visual/mnist-sample.ts:
--------------------------------------------------------------------------------
1 | export const ministSampleData = [
2 | [
3 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
5 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
6 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7 | 0.133, 0.663, 0.98, 0.157, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
8 | 0, 0, 0, 0, 0, 0, 0, 0.227, 0.949, 0.867, 0.561, 0.067, 0, 0, 0, 0, 0, 0, 0,
9 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.294, 0.969, 0.561, 0.039, 0,
10 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.145,
11 | 0.961, 0.722, 0.008, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
12 | 0, 0, 0, 0, 0, 0.031, 0.753, 0.784, 0.055, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
13 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.545, 0.969, 0.11, 0, 0, 0, 0, 0, 0,
14 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.027, 0.906, 0.718,
15 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
16 | 0.49, 0.953, 0.196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
17 | 0, 0, 0, 0, 0, 0, 0.765, 0.722, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
18 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.239, 0.984, 0.161, 0, 0, 0, 0.251, 0.169, 0,
19 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.596, 0.824,
20 | 0.027, 0, 0.376, 0.929, 0.996, 0.969, 0.42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
21 | 0, 0, 0, 0, 0, 0, 0, 0, 0.98, 0.329, 0, 0.024, 0.875, 0.329, 0.051, 0.341,
22 | 0.965, 0.282, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.169,
23 | 0.996, 0.314, 0, 0.22, 0.592, 0, 0, 0, 0.576, 0.757, 0, 0, 0, 0, 0, 0, 0, 0,
24 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.263, 0.996, 0.161, 0, 0.051, 0.075, 0, 0, 0,
25 | 0.165, 0.992, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.263,
26 | 0.996, 0.051, 0, 0, 0, 0, 0, 0, 0.055, 0.992, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
27 | 0, 0, 0, 0, 0, 0, 0, 0.267, 1, 0.051, 0, 0, 0, 0, 0, 0, 0.302, 0.941, 0, 0,
28 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.263, 0.996, 0.051, 0, 0, 0,
29 | 0, 0, 0.02, 0.71, 0.576, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
30 | 0.098, 0.898, 0.412, 0, 0, 0, 0, 0.02, 0.612, 0.835, 0.078, 0, 0, 0, 0, 0,
31 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.42, 0.965, 0.412, 0.055, 0.192,
32 | 0.373, 0.851, 0.82, 0.106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
33 | 0, 0, 0, 0, 0.42, 0.965, 0.992, 0.992, 0.941, 0.51, 0.024, 0, 0, 0, 0, 0, 0,
34 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
35 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
36 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
37 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
38 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
39 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
40 | ],
41 | [
42 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
43 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
44 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
45 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
46 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
47 | 0.569, 1, 0.827, 0.122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
48 | 0, 0, 0, 0, 0, 0, 0.125, 0.929, 0.992, 0.988, 0.278, 0, 0, 0, 0, 0, 0, 0, 0,
49 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.043, 0.686, 0.992, 0.988,
50 | 0.278, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
51 | 0, 0.565, 0.992, 0.988, 0.278, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
52 | 0, 0, 0, 0, 0, 0, 0, 0, 0.063, 0.749, 0.992, 0.988, 0.278, 0, 0, 0, 0, 0, 0,
53 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.102, 0.867, 0.992,
54 | 0.988, 0.486, 0.122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55 | 0, 0, 0, 0, 0, 0.49, 0.992, 0.988, 0.988, 0.424, 0, 0, 0, 0, 0, 0, 0, 0, 0,
56 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.992, 0.988, 0.988, 0.424, 0,
57 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
58 | 0.992, 0.992, 0.424, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
59 | 0, 0, 0, 0, 0, 0, 0.992, 0.988, 0.988, 0.424, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.992, 0.988, 0.988, 0.424, 0, 0,
61 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.992,
62 | 0.988, 0.988, 0.424, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63 | 0, 0, 0, 0, 0, 0, 1, 0.992, 0.992, 0.667, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
64 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.992, 0.988, 0.988, 0.988, 0.165, 0,
65 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.584,
66 | 0.988, 0.988, 0.988, 0.565, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
67 | 0, 0, 0, 0, 0, 0, 0, 0.427, 0.988, 0.988, 0.988, 0.565, 0, 0, 0, 0, 0, 0, 0,
68 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.855, 0.992, 0.992, 1,
69 | 0.137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
70 | 0.686, 0.988, 0.988, 0.992, 0.137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
71 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.286, 0.988, 0.988, 0.992, 0.137, 0, 0, 0, 0, 0,
72 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.122, 0.827, 0.988,
73 | 0.992, 0.137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
74 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
75 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
76 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
77 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
78 | ],
79 | [
80 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
81 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
82 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
83 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
84 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
85 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
86 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
87 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
88 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.122, 0.157, 0.506, 0.918,
89 | 0.918, 0.624, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
90 | 0.267, 0.588, 0.937, 0.996, 0.992, 0.992, 0.992, 0.843, 0, 0, 0, 0, 0, 0, 0,
91 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.612, 0.788, 0.996, 0.996, 0.996, 0.945,
92 | 0.588, 0.384, 0.031, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
93 | 0.075, 0.604, 0.996, 0.925, 0.796, 0.325, 0.153, 0.118, 0, 0, 0, 0, 0, 0, 0,
94 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.565, 0.992, 0.569, 0.047, 0, 0, 0,
95 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.039, 0.506, 0.871,
96 | 0.306, 0.31, 0.031, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
97 | 0, 0, 0, 0.525, 0.992, 0.655, 0.031, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
98 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0.996, 0.306, 0, 0, 0, 0, 0, 0, 0, 0, 0,
99 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.788, 0.992, 0.886, 0.271,
100 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.216, 0.024,
101 | 0, 0.071, 0.502, 0.992, 0.945, 0.161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
102 | 0, 0, 0, 0, 0, 0.098, 0.804, 0.922, 0.361, 0, 0, 0.078, 0.992, 0.992, 0.227,
103 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.906, 0.961, 0.424,
104 | 0, 0, 0, 0.518, 0.992, 0.725, 0.055, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
105 | 0, 0, 0, 0, 0, 0.475, 0.961, 0.996, 0.996, 0.996, 0.851, 0.996, 0.875,
106 | 0.196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.455,
107 | 0.647, 0.914, 0.914, 0.918, 0.706, 0.153, 0.012, 0, 0, 0, 0, 0, 0, 0, 0, 0,
108 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
109 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
110 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
111 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
112 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
113 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
114 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
115 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
116 | ],
117 | [
118 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
119 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
120 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
121 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
122 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123 | 0, 0, 0, 0, 0.251, 0.992, 1, 0.247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.376, 0.804, 0.984, 0.992, 0.804, 0.435,
125 | 0.016, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.376,
126 | 0.741, 0.984, 0.984, 0.992, 0.984, 0.984, 0.122, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127 | 0, 0, 0, 0, 0, 0, 0, 0, 0.063, 0.251, 0.875, 0.957, 0.984, 0.984, 0.827,
128 | 0.835, 0.984, 0.984, 0.122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
129 | 0.314, 0.71, 0.984, 0.992, 0.984, 0.984, 0.984, 0.369, 0.376, 0.984, 0.984,
130 | 0.122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.361, 0.992, 0.992,
131 | 0.992, 1, 0.992, 0.992, 0.992, 0.373, 0.376, 0.992, 0.992, 0.122, 0, 0, 0,
132 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.361, 0.925, 0.984, 0.953, 0.863, 0.914,
133 | 0.984, 0.984, 0.953, 0.322, 0.376, 0.984, 0.984, 0.122, 0, 0, 0, 0, 0, 0, 0,
134 | 0, 0, 0, 0, 0, 0, 0.314, 0.992, 0.984, 0.984, 0.737, 0, 0.376, 0.984, 0.984,
135 | 0.427, 0, 0.376, 0.984, 0.984, 0.122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
136 | 0.376, 0.941, 0.992, 0.953, 0.737, 0.165, 0, 0.376, 0.8, 0.427, 0.016, 0,
137 | 0.047, 0.773, 0.984, 0.122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.867,
138 | 0.984, 0.992, 0.475, 0, 0, 0, 0.141, 0.09, 0, 0, 0, 0, 0.745, 0.984, 0.122,
139 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.188, 0.918, 0.992, 0, 0, 0, 0, 0, 0, 0,
140 | 0, 0, 0, 0, 0.749, 0.992, 0.122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.173, 0.867,
141 | 0.984, 0.984, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.047, 0.773, 0.984, 0.122, 0,
142 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.745, 0.984, 0.984, 0.984, 0, 0, 0, 0, 0, 0, 0,
143 | 0, 0, 0, 0.376, 0.984, 0.984, 0.122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.745,
144 | 0.984, 0.984, 0.443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.157, 0.918, 0.984, 0.859,
145 | 0.09, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.745, 0.984, 0.984, 0.369, 0, 0, 0, 0,
146 | 0, 0, 0, 0, 0.157, 0.851, 0.992, 0.906, 0.184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
147 | 0, 0.749, 0.992, 0.992, 0.992, 0, 0, 0, 0, 0, 0, 0.047, 0.682, 0.992, 0.992,
148 | 0.859, 0.153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.263, 0.925, 0.984,
149 | 0.984, 0.749, 0.745, 0.435, 0.282, 0.745, 0.749, 0.773, 0.984, 0.953, 0.475,
150 | 0.153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.247, 0.925, 0.984, 0.992,
151 | 0.984, 0.984, 0.984, 0.984, 0.992, 0.984, 0.737, 0.369, 0, 0, 0, 0, 0, 0, 0,
152 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.106, 0.506, 0.992, 0.984, 0.984, 0.984,
153 | 0.984, 0.898, 0.659, 0.059, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
154 | 0, 0, 0, 0, 0.373, 0.831, 0.984, 0.827, 0.369, 0.231, 0, 0, 0, 0, 0, 0, 0,
155 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
156 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
157 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
158 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
159 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
160 | ],
161 | [
162 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
163 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
164 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
165 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
166 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
167 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
168 | 0, 0.365, 0.643, 0.827, 0.98, 0.98, 0.761, 0.059, 0, 0, 0, 0, 0, 0, 0, 0, 0,
169 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.078, 0.69, 0.992, 0.929, 0.706, 0.706,
170 | 0.953, 0.996, 0.839, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
171 | 0, 0.8, 0.925, 0.529, 0.071, 0, 0, 0.157, 0.949, 0.988, 0.494, 0, 0, 0, 0,
172 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.271, 0.992, 0.655, 0, 0, 0, 0, 0,
173 | 0.51, 0.996, 0.875, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.29,
174 | 0.851, 0.31, 0, 0, 0, 0, 0, 0.18, 0.996, 0.906, 0.055, 0, 0, 0, 0, 0, 0, 0,
175 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.031, 0.039, 0, 0, 0, 0, 0, 0, 0.153, 0.996,
176 | 0.996, 0.408, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
177 | 0, 0, 0, 0.02, 0.831, 0.996, 0.553, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
178 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.812, 0.996, 0.553, 0, 0, 0, 0, 0, 0,
179 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.027, 0.843, 0.996,
180 | 0.502, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
181 | 0, 0.153, 0.996, 0.996, 0.22, 0, 0, 0.078, 0.263, 0.486, 0.153, 0, 0, 0, 0,
182 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.027, 0.137, 0.384, 0.996, 0.996,
183 | 0.816, 0.616, 0.812, 0.882, 0.996, 0.945, 0.627, 0, 0, 0, 0, 0, 0, 0, 0, 0,
184 | 0, 0.035, 0.122, 0.322, 0.537, 0.796, 0.796, 0.831, 0.996, 0.996, 0.996,
185 | 0.996, 0.984, 0.875, 0.875, 0.498, 0.204, 0.129, 0, 0, 0, 0, 0, 0, 0, 0, 0,
186 | 0.035, 0.537, 0.839, 0.996, 0.996, 0.996, 0.996, 0.941, 0.894, 0.98, 0.996,
187 | 0.996, 0.604, 0.196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.725, 0.996,
188 | 0.969, 0.702, 0.573, 0.263, 0.235, 0.11, 0, 0.847, 0.996, 0.863, 0.047, 0,
189 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0.871, 0.192, 0, 0, 0, 0,
190 | 0.016, 0.537, 0.957, 0.91, 0.196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
191 | 0, 0, 0.996, 0.808, 0.016, 0, 0, 0, 0.031, 0.702, 0.996, 0.969, 0.251, 0, 0,
192 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.847, 0.996, 0.62, 0.694,
193 | 0.51, 0.376, 0.835, 0.988, 0.78, 0.192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
194 | 0, 0, 0, 0, 0, 0, 0, 0.514, 0.969, 0.976, 0.976, 0.976, 0.671, 0.282, 0, 0,
195 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
196 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
197 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
198 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
199 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
200 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
201 | 0, 0,
202 | ],
203 | [
204 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
205 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
206 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
208 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
209 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
210 | 0, 0, 0, 0.165, 0.604, 0.706, 1, 0.69, 0.463, 0.463, 0.063, 0, 0, 0, 0, 0,
211 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.557, 0.992, 0.992, 0.992,
212 | 0.992, 0.992, 0.992, 0.925, 0.404, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
213 | 0, 0, 0, 0, 0, 0.89, 0.992, 0.992, 0.8, 0.694, 0.694, 0.694, 0.953, 0.749,
214 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.89, 0.992, 0.847,
215 | 0.086, 0, 0, 0.09, 0.89, 0.933, 0.376, 0.082, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
216 | 0, 0, 0, 0, 0, 0, 0, 0.89, 0.992, 0.804, 0, 0, 0.067, 0.486, 0.992, 0.992,
217 | 0.992, 0.667, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.89,
218 | 0.992, 0.918, 0.243, 0.071, 0.788, 0.992, 0.992, 0.992, 0.984, 0.353, 0, 0,
219 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.89, 0.992, 0.992, 0.992,
220 | 0.992, 0.992, 0.992, 0.992, 0.867, 0.404, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
221 | 0, 0, 0, 0, 0, 0, 0, 0.89, 0.992, 0.992, 0.992, 0.992, 0.992, 0.816, 0.094,
222 | 0.02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.153, 0.925,
223 | 0.992, 0.992, 0.992, 0.984, 0.38, 0.063, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
224 | 0, 0, 0, 0, 0, 0, 0.016, 0.271, 0.878, 0.992, 0.992, 0.941, 0.663, 0.18, 0,
225 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.129, 0.525, 0.992,
226 | 0.992, 0.992, 0.992, 0.412, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
227 | 0, 0, 0, 0, 0.2, 0.882, 0.992, 0.992, 0.992, 0.992, 0.992, 0.267, 0, 0, 0,
228 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.188, 0.89, 0.992, 0.992,
229 | 0.98, 0.682, 0.992, 0.992, 0.267, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
230 | 0, 0, 0, 0, 0, 0.835, 0.992, 0.992, 0.702, 0.247, 0.435, 0.992, 0.992,
231 | 0.267, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.361, 0.984,
232 | 0.788, 0.051, 0.02, 0, 0.651, 0.992, 0.992, 0.267, 0, 0, 0, 0, 0, 0, 0, 0,
233 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.153, 0.871, 0.992, 0.776, 0, 0, 0, 0.973,
234 | 0.992, 0.906, 0.18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
235 | 0.212, 0.953, 0.992, 0.486, 0, 0.149, 0.522, 0.988, 0.992, 0.588, 0, 0, 0,
236 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.463, 0.992, 0.992, 0.929,
237 | 0.702, 0.875, 0.992, 0.992, 0.745, 0.055, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
238 | 0, 0, 0, 0, 0, 0, 0, 0.173, 0.902, 0.992, 0.992, 0.992, 0.992, 0.992, 0.957,
239 | 0.298, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.176,
240 | 0.906, 0.992, 0.992, 0.992, 0.714, 0.259, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
241 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
242 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
243 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
244 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
245 | ],
246 | [
247 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
248 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
249 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
250 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
251 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
252 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
253 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
254 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
255 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
256 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.012, 0.145, 0.145, 0.145,
257 | 0.027, 0, 0, 0, 0, 0, 0, 0, 0, 0.306, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.055,
258 | 0.329, 0.714, 0.737, 0.757, 0.996, 0.996, 0.996, 0.486, 0, 0, 0, 0, 0, 0, 0,
259 | 0, 0.035, 0, 0, 0, 0, 0, 0, 0, 0.086, 0.357, 0.51, 0.757, 0.996, 0.996, 0.8,
260 | 0.49, 0.788, 0.996, 0.996, 0.965, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
261 | 0, 0, 0.839, 0.996, 1, 0.996, 1, 0.678, 0.086, 0, 0.384, 0.996, 1, 0.596, 0,
262 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.835, 0.961, 0.776, 0.294,
263 | 0.122, 0.008, 0, 0.459, 0.961, 0.996, 0.867, 0.098, 0, 0, 0, 0, 0, 0, 0, 0,
264 | 0, 0, 0, 0, 0, 0, 0, 0, 0.282, 0.141, 0, 0, 0, 0, 0.306, 0.965, 0.996,
265 | 0.871, 0.129, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
266 | 0, 0.459, 0.953, 0.996, 0.882, 0.145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
267 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.302, 0.976, 0.996, 0.863, 0.286, 0, 0, 0, 0, 0,
268 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.294, 0.949, 0.996,
269 | 0.878, 0.145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
270 | 0, 0.294, 0.984, 0.996, 0.859, 0.129, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
271 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.302, 0.949, 0.996, 0.996, 0.157, 0, 0, 0, 0, 0,
272 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.114, 0.82, 0.996, 0.91,
273 | 0.325, 0.004, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
274 | 0.078, 0.886, 0.996, 0.878, 0.247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
275 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.247, 0.996, 0.98, 0.227, 0, 0, 0, 0, 0, 0, 0,
276 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.184, 0.957, 0.682, 0,
277 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
278 | 0.118, 0.055, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
279 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
280 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
281 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
282 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
283 | 0, 0, 0, 0, 0, 0, 0, 0,
284 | ],
285 | [
286 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
287 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
288 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
289 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
290 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.165,
291 | 0.463, 0.859, 0.651, 0.463, 0.463, 0.024, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
292 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.404, 0.949, 0.996, 0.996, 0.996, 0.996, 0.996,
293 | 0.259, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.071,
294 | 0.91, 0.996, 0.996, 0.996, 0.996, 0.996, 0.933, 0.275, 0, 0, 0, 0, 0, 0, 0,
295 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.408, 0.957, 0.996, 0.878, 0.996,
296 | 0.996, 0.996, 0.553, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
297 | 0, 0, 0, 0.812, 0.996, 0.824, 0.996, 0.996, 0.996, 0.133, 0, 0, 0, 0, 0, 0,
298 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.329, 0.808, 0.996, 0.996,
299 | 0.996, 0.996, 0.161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
300 | 0, 0, 0, 0, 0.094, 0.82, 0.996, 0.996, 0.996, 0.671, 0, 0, 0, 0, 0, 0, 0, 0,
301 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.357, 0.537, 0.992, 0.996, 0.996,
302 | 0.996, 0.439, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
303 | 0.157, 0.839, 0.98, 0.996, 0.996, 0.996, 0.996, 0.996, 0.133, 0, 0, 0, 0, 0,
304 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.318, 0.969, 0.996, 0.996, 0.996,
305 | 0.996, 0.996, 0.996, 0.573, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
306 | 0, 0, 0, 0, 0.431, 0.965, 0.996, 0.996, 0.996, 0.996, 0.996, 0.671, 0, 0, 0,
307 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.286, 0.349, 0.349,
308 | 0.365, 0.941, 0.996, 0.671, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
309 | 0, 0, 0, 0, 0, 0, 0, 0, 0.004, 0.502, 0.996, 0.859, 0.122, 0, 0, 0, 0, 0, 0,
310 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.027, 0.996, 0.996,
311 | 0.839, 0.11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
312 | 0, 0, 0.541, 0.996, 0.996, 0.455, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
313 | 0, 0.075, 0.694, 0.353, 0, 0, 0, 0, 0, 0.098, 0.941, 0.996, 0.996, 0.133, 0,
314 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.643, 0.996, 0.843, 0.247, 0.141,
315 | 0, 0.2, 0.349, 0.808, 0.996, 0.996, 0.545, 0.031, 0, 0, 0, 0, 0, 0, 0, 0, 0,
316 | 0, 0, 0, 0, 0, 0, 0.224, 0.773, 0.996, 0.996, 0.871, 0.706, 0.945, 0.996,
317 | 0.996, 0.992, 0.835, 0.043, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
318 | 0, 0.549, 0.412, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.925, 0, 0, 0,
319 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.027, 0.459, 0.459,
320 | 0.647, 0.996, 0.996, 0.937, 0.196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
321 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
322 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
323 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
324 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
325 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
326 | ],
327 | [
328 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
329 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
330 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
331 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
332 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
333 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
334 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
335 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
336 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.071, 0.412, 0.89, 0.992, 0.992, 0.478, 0, 0, 0,
337 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.224, 0.78, 0.992,
338 | 0.988, 0.988, 0.988, 0.988, 0.624, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
339 | 0, 0, 0, 0, 0, 0.078, 0.827, 0.988, 0.91, 0.596, 0.286, 0.655, 0.988, 0.843,
340 | 0.024, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.773, 0.988,
341 | 0.714, 0, 0, 0, 0.145, 0.922, 0.953, 0.184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
342 | 0, 0, 0, 0, 0, 0, 0, 0.737, 0.988, 0.404, 0, 0, 0, 0.145, 0.922, 0.898,
343 | 0.106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.741, 0.992,
344 | 0.337, 0.031, 0.169, 0.545, 0.745, 0.827, 0.176, 0, 0, 0, 0, 0, 0, 0, 0, 0,
345 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.91, 0.988, 0.784, 0.788, 0.988, 0.988,
346 | 0.329, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.835,
347 | 0.961, 0.988, 0.992, 0.988, 0.949, 0.165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
348 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.22, 0.329, 0.992, 0.988, 0.627, 0, 0, 0,
349 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.176, 0.992,
350 | 0.988, 0.149, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
351 | 0, 0, 0, 0.349, 1, 0.992, 0.149, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
352 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.314, 0.992, 0.741, 0.125, 0, 0, 0, 0, 0, 0,
353 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.161, 0.702, 0.91,
354 | 0.329, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
355 | 0.059, 0.882, 0.988, 0.451, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
356 | 0, 0, 0, 0, 0, 0, 0, 0, 0.6, 0.988, 0.643, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
357 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.267, 0.961, 0.953, 0.31, 0, 0, 0,
358 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.125, 0.929,
359 | 0.961, 0.322, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
360 | 0, 0, 0.035, 0.58, 0.988, 0.663, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
361 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.416, 0.992, 0.769, 0.027, 0, 0, 0, 0, 0, 0,
362 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.212, 0.894, 0.506,
363 | 0.11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
364 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
365 | ],
366 | [
367 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
368 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
369 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
370 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
371 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
372 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.741, 0.745, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
373 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.561, 0.969, 0.6, 0, 0, 0, 0, 0, 0,
374 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.533, 0.969, 0.949,
375 | 0.337, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
376 | 0, 0.753, 0.988, 0.733, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
377 | 0.243, 0.725, 0.071, 0, 0, 0, 0, 0.349, 0.925, 0.851, 0.184, 0, 0, 0, 0, 0,
378 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.847, 0.992, 0.235, 0, 0, 0, 0, 0.831,
379 | 1, 0.318, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.808,
380 | 0.988, 0.267, 0, 0, 0, 0.188, 0.949, 0.992, 0.349, 0, 0, 0, 0, 0, 0, 0, 0,
381 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.514, 0.984, 0.831, 0.082, 0, 0, 0.043, 0.655,
382 | 0.988, 0.773, 0.02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.114,
383 | 0.91, 0.969, 0.247, 0, 0, 0, 0.6, 0.988, 0.886, 0, 0, 0, 0, 0, 0, 0, 0, 0,
384 | 0, 0, 0, 0, 0, 0, 0, 0, 0.176, 0.859, 0.988, 0.561, 0, 0, 0, 0.455, 0.976,
385 | 0.988, 0.404, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.016, 0.376,
386 | 0.992, 1, 0.992, 0.784, 0.478, 0.027, 0.098, 0.788, 0.98, 0.62, 0, 0, 0, 0,
387 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.361, 0.988, 0.988, 0.992, 0.851,
388 | 0.988, 0.988, 0.784, 0.89, 0.988, 0.906, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
389 | 0, 0, 0, 0, 0.341, 0.984, 0.969, 0.906, 0.255, 0.188, 0.741, 0.988, 0.988,
390 | 0.992, 0.988, 0.984, 0.89, 0.137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
391 | 0.745, 0.867, 0.384, 0, 0, 0, 0.165, 0.769, 0.988, 0.992, 0.988, 0.988,
392 | 0.635, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.435, 0.114, 0, 0, 0,
393 | 0, 0.243, 0.937, 0.988, 0.337, 0.165, 0.165, 0.055, 0, 0, 0, 0, 0, 0, 0, 0,
394 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.059, 0.58, 0.992, 0.855, 0, 0, 0, 0,
395 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.475, 0.988,
396 | 0.906, 0.11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
397 | 0, 0, 0.122, 0.867, 0.984, 0.506, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
398 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.855, 0.988, 0.627, 0, 0, 0, 0, 0, 0, 0, 0,
399 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.478, 0.988, 0.322, 0,
400 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
401 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
402 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
403 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
404 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
405 | 0,
406 | ],
407 | ];
408 |
--------------------------------------------------------------------------------