├── .npmrc ├── src ├── .npmignore ├── OutputLayer.js ├── activationFunctions.js ├── Layer.js ├── __tests__ │ └── test.js └── FeedForwardNeuralNetwork.js ├── .babelrc ├── .eslintrc.yml ├── .gitignore ├── .travis.yml ├── docs ├── assets │ ├── fonts │ │ ├── EOT │ │ │ ├── SourceCodePro-Bold.eot │ │ │ └── SourceCodePro-Regular.eot │ │ ├── OTF │ │ │ ├── SourceCodePro-Bold.otf │ │ │ └── SourceCodePro-Regular.otf │ │ ├── TTF │ │ │ ├── SourceCodePro-Bold.ttf │ │ │ └── SourceCodePro-Regular.ttf │ │ ├── WOFF │ │ │ ├── OTF │ │ │ │ ├── SourceCodePro-Bold.otf.woff │ │ │ │ └── SourceCodePro-Regular.otf.woff │ │ │ └── TTF │ │ │ │ ├── SourceCodePro-Bold.ttf.woff │ │ │ │ └── SourceCodePro-Regular.ttf.woff │ │ ├── WOFF2 │ │ │ ├── OTF │ │ │ │ ├── SourceCodePro-Bold.otf.woff2 │ │ │ │ └── SourceCodePro-Regular.otf.woff2 │ │ │ └── TTF │ │ │ │ ├── SourceCodePro-Bold.ttf.woff2 │ │ │ │ └── SourceCodePro-Regular.ttf.woff2 │ │ ├── source-code-pro.css │ │ └── LICENSE.txt │ ├── bass-addons.css │ ├── split.css │ ├── github.css │ ├── style.css │ ├── site.js │ ├── bass.css │ ├── anchor.js │ └── split.js └── index.html ├── rollup.config.js ├── README.md ├── LICENSE ├── package.json └── History.md /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /src/.npmignore: -------------------------------------------------------------------------------- 1 | __tests__ 2 | .npmignore 3 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@babel/plugin-transform-modules-commonjs"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | extends: cheminfo 2 | parserOptions: 3 | sourceType: module 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | coveralls 4 | *.log 5 | FeedForwardNeuralNetwork.js 6 | coverage 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 12 4 | - 10 5 | - 8 6 | after_success: 7 | - bash <(curl -s https://codecov.io/bash) 8 | -------------------------------------------------------------------------------- /docs/assets/fonts/EOT/SourceCodePro-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mljs/feedforward-neural-networks/HEAD/docs/assets/fonts/EOT/SourceCodePro-Bold.eot -------------------------------------------------------------------------------- /docs/assets/fonts/OTF/SourceCodePro-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mljs/feedforward-neural-networks/HEAD/docs/assets/fonts/OTF/SourceCodePro-Bold.otf -------------------------------------------------------------------------------- /docs/assets/fonts/TTF/SourceCodePro-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mljs/feedforward-neural-networks/HEAD/docs/assets/fonts/TTF/SourceCodePro-Bold.ttf -------------------------------------------------------------------------------- /docs/assets/fonts/EOT/SourceCodePro-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mljs/feedforward-neural-networks/HEAD/docs/assets/fonts/EOT/SourceCodePro-Regular.eot -------------------------------------------------------------------------------- /docs/assets/fonts/OTF/SourceCodePro-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mljs/feedforward-neural-networks/HEAD/docs/assets/fonts/OTF/SourceCodePro-Regular.otf -------------------------------------------------------------------------------- /docs/assets/fonts/TTF/SourceCodePro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mljs/feedforward-neural-networks/HEAD/docs/assets/fonts/TTF/SourceCodePro-Regular.ttf -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF/OTF/SourceCodePro-Bold.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mljs/feedforward-neural-networks/HEAD/docs/assets/fonts/WOFF/OTF/SourceCodePro-Bold.otf.woff -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF/TTF/SourceCodePro-Bold.ttf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mljs/feedforward-neural-networks/HEAD/docs/assets/fonts/WOFF/TTF/SourceCodePro-Bold.ttf.woff -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF/OTF/SourceCodePro-Regular.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mljs/feedforward-neural-networks/HEAD/docs/assets/fonts/WOFF/OTF/SourceCodePro-Regular.otf.woff -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF/TTF/SourceCodePro-Regular.ttf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mljs/feedforward-neural-networks/HEAD/docs/assets/fonts/WOFF/TTF/SourceCodePro-Regular.ttf.woff -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF2/OTF/SourceCodePro-Bold.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mljs/feedforward-neural-networks/HEAD/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Bold.otf.woff2 -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF2/TTF/SourceCodePro-Bold.ttf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mljs/feedforward-neural-networks/HEAD/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Bold.ttf.woff2 -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF2/OTF/SourceCodePro-Regular.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mljs/feedforward-neural-networks/HEAD/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Regular.otf.woff2 -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mljs/feedforward-neural-networks/HEAD/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2 -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | input: 'src/FeedForwardNeuralNetwork.js', 3 | output: { 4 | format: 'cjs', 5 | file: 'FeedForwardNeuralNetwork.js' 6 | }, 7 | external: ['ml-matrix'] 8 | }; 9 | -------------------------------------------------------------------------------- /docs/assets/bass-addons.css: -------------------------------------------------------------------------------- 1 | .input { 2 | font-family: inherit; 3 | display: block; 4 | width: 100%; 5 | height: 2rem; 6 | padding: .5rem; 7 | margin-bottom: 1rem; 8 | border: 1px solid #ccc; 9 | font-size: .875rem; 10 | border-radius: 3px; 11 | box-sizing: border-box; 12 | } 13 | -------------------------------------------------------------------------------- /src/OutputLayer.js: -------------------------------------------------------------------------------- 1 | import { Layer } from './Layer'; 2 | 3 | export class OutputLayer extends Layer { 4 | constructor(options) { 5 | super(options); 6 | 7 | this.activationFunction = function (i, j) { 8 | this.set(i, j, Math.exp(this.get(i, j))); 9 | }; 10 | } 11 | 12 | static load(model) { 13 | if (model.model !== 'Layer') { 14 | throw new RangeError('the current model is not a Layer model'); 15 | } 16 | 17 | return new OutputLayer(model); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /docs/assets/split.css: -------------------------------------------------------------------------------- 1 | .gutter { 2 | background-color: #f5f5f5; 3 | background-repeat: no-repeat; 4 | background-position: 50%; 5 | } 6 | 7 | .gutter.gutter-vertical { 8 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFAQMAAABo7865AAAABlBMVEVHcEzMzMzyAv2sAAAAAXRSTlMAQObYZgAAABBJREFUeF5jOAMEEAIEEFwAn3kMwcB6I2AAAAAASUVORK5CYII='); 9 | cursor: ns-resize; 10 | } 11 | 12 | .gutter.gutter-horizontal { 13 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg=='); 14 | cursor: ew-resize; 15 | } 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Feedforward Neural Network 2 | 3 | [![NPM version][npm-image]][npm-url] 4 | [![build status][travis-image]][travis-url] 5 | [![npm download][download-image]][download-url] 6 | 7 | A implementation of feedforward neural networks in javascript based on wildml [implementation](http://www.wildml.com/2015/09/implementing-a-neural-network-from-scratch/). 8 | 9 | ## Installation 10 | 11 | `$ npm i ml-fnn` 12 | 13 | ## [API Documentation](https://mljs.github.io/feedforward-neural-networks/) 14 | 15 | ## License 16 | 17 | [MIT](./LICENSE) 18 | 19 | [npm-image]: https://img.shields.io/npm/v/ml-fnn.svg?style=flat-square 20 | [npm-url]: https://npmjs.org/package/ml-fnn 21 | [travis-image]: https://img.shields.io/travis/mljs/feedforward-neural-networks/master.svg?style=flat-square 22 | [travis-url]: https://travis-ci.org/mljs/feedforward-neural-networks 23 | [download-image]: https://img.shields.io/npm/dm/ml-fnn.svg?style=flat-square 24 | [download-url]: https://npmjs.org/package/ml-fnn 25 | -------------------------------------------------------------------------------- /docs/assets/fonts/source-code-pro.css: -------------------------------------------------------------------------------- 1 | @font-face{ 2 | font-family: 'Source Code Pro'; 3 | font-weight: 400; 4 | font-style: normal; 5 | font-stretch: normal; 6 | src: url('EOT/SourceCodePro-Regular.eot') format('embedded-opentype'), 7 | url('WOFF2/TTF/SourceCodePro-Regular.ttf.woff2') format('woff2'), 8 | url('WOFF/OTF/SourceCodePro-Regular.otf.woff') format('woff'), 9 | url('OTF/SourceCodePro-Regular.otf') format('opentype'), 10 | url('TTF/SourceCodePro-Regular.ttf') format('truetype'); 11 | } 12 | 13 | @font-face{ 14 | font-family: 'Source Code Pro'; 15 | font-weight: 700; 16 | font-style: normal; 17 | font-stretch: normal; 18 | src: url('EOT/SourceCodePro-Bold.eot') format('embedded-opentype'), 19 | url('WOFF2/TTF/SourceCodePro-Bold.ttf.woff2') format('woff2'), 20 | url('WOFF/OTF/SourceCodePro-Bold.otf.woff') format('woff'), 21 | url('OTF/SourceCodePro-Bold.otf') format('opentype'), 22 | url('TTF/SourceCodePro-Bold.ttf') format('truetype'); 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 ml.js 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ml-fnn", 3 | "version": "5.0.0", 4 | "description": "feedforward neural networks library", 5 | "main": "FeedForwardNeuralNetwork.js", 6 | "files": [ 7 | "src", 8 | "FeedForwardNeuralNetwork.js" 9 | ], 10 | "scripts": { 11 | "compile": "rollup -c", 12 | "eslint": "eslint src", 13 | "eslint-fix": "npm run eslint -- --fix", 14 | "prepublishOnly": "npm run compile", 15 | "test": "npm run test-coverage && npm run eslint", 16 | "test-only": "jest", 17 | "test-coverage": "jest --coverage" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/mljs/feedforward-neural-networks.git" 22 | }, 23 | "keywords": [ 24 | "neural", 25 | "networks", 26 | "feedforward", 27 | "machine", 28 | "learning" 29 | ], 30 | "author": "Jefferson Hernández", 31 | "license": "MIT", 32 | "bugs": { 33 | "url": "https://github.com/mljs/feedforward-neural-networks/issues" 34 | }, 35 | "homepage": "https://github.com/mljs/feedforward-neural-networks", 36 | "dependencies": { 37 | "ml-matrix": "^6.1.2" 38 | }, 39 | "devDependencies": { 40 | "@babel/plugin-transform-modules-commonjs": "^7.4.4", 41 | "eslint": "^6.0.1", 42 | "eslint-config-cheminfo": "^1.20.1", 43 | "eslint-plugin-import": "^2.18.0", 44 | "eslint-plugin-jest": "^22.7.1", 45 | "jest": "^24.8.0", 46 | "rollup": "^1.16.3" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | # [5.0.0](https://github.com/mljs/feedforward-neural-networks/compare/v4.0.0...v5.0.0) (2019-06-29) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * test 'Big test cas 2' is ignored, because won't never work with current implementation ([5a92c34](https://github.com/mljs/feedforward-neural-networks/commit/5a92c34)) 7 | 8 | 9 | ### chore 10 | 11 | * update dependencies and remove support for Node.js 6 ([bb4516d](https://github.com/mljs/feedforward-neural-networks/commit/bb4516d)) 12 | 13 | 14 | ### Features 15 | 16 | * Add the possibility to retrain a pre-trained model ([db995e4](https://github.com/mljs/feedforward-neural-networks/commit/db995e4)) 17 | 18 | 19 | ### BREAKING CHANGES 20 | 21 | * Node.js 6 is no longer supported. 22 | 23 | 24 | 25 | 26 | # [4.0.0](https://github.com/mljs/feedforward-neural-networks/compare/v3.0.0...v4.0.0) (2017-07-21) 27 | 28 | 29 | ### Chores 30 | 31 | * remove support for Node 4 ([a0d74da](https://github.com/mljs/feedforward-neural-networks/commit/a0d74da)) 32 | 33 | 34 | ### BREAKING CHANGES 35 | 36 | * Removed support for Node 4 37 | 38 | 39 | 40 | 41 | # [3.0.0](https://github.com/mljs/feedforward-neural-networks/compare/v2.1.1...v3.0.0) (2016-12-17) 42 | 43 | 44 | ### Features 45 | 46 | * add tanh non-linearity for layers ([#3](https://github.com/mljs/feedforward-neural-networks/issues/3)) ([bde6cd5](https://github.com/mljs/feedforward-neural-networks/commit/bde6cd5)) 47 | * **ActivationFunctions:** new activation functions ([6ae5448](https://github.com/mljs/feedforward-neural-networks/commit/6ae5448)) 48 | * **ActivationFunctions:** new activation functions ([4c24121](https://github.com/mljs/feedforward-neural-networks/commit/4c24121)) 49 | * **FeedForwardNeuralNetwork:** parametric activation functions added. ([ec89a4e](https://github.com/mljs/feedforward-neural-networks/commit/ec89a4e)) 50 | 51 | 52 | 53 | 54 | ## [2.1.1](https://github.com/mljs/feedforward-neural-networks/compare/v2.0.0...v2.1.1) (2016-08-16) 55 | 56 | 57 | 58 | 59 | # [2.0.0](https://github.com/mljs/feedforward-neural-networks/compare/v1.0.1...v2.0.0) (2016-06-09) 60 | 61 | 62 | 63 | 64 | ## 1.0.1 (2015-11-19) 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /docs/assets/github.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | github.com style (c) Vasily Polovnyov 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | color: #333; 12 | background: #f8f8f8; 13 | -webkit-text-size-adjust: none; 14 | } 15 | 16 | .hljs-comment, 17 | .diff .hljs-header, 18 | .hljs-javadoc { 19 | color: #998; 20 | font-style: italic; 21 | } 22 | 23 | .hljs-keyword, 24 | .css .rule .hljs-keyword, 25 | .hljs-winutils, 26 | .nginx .hljs-title, 27 | .hljs-subst, 28 | .hljs-request, 29 | .hljs-status { 30 | color: #1184CE; 31 | } 32 | 33 | .hljs-number, 34 | .hljs-hexcolor, 35 | .ruby .hljs-constant { 36 | color: #ed225d; 37 | } 38 | 39 | .hljs-string, 40 | .hljs-tag .hljs-value, 41 | .hljs-phpdoc, 42 | .hljs-dartdoc, 43 | .tex .hljs-formula { 44 | color: #ed225d; 45 | } 46 | 47 | .hljs-title, 48 | .hljs-id, 49 | .scss .hljs-preprocessor { 50 | color: #900; 51 | font-weight: bold; 52 | } 53 | 54 | .hljs-list .hljs-keyword, 55 | .hljs-subst { 56 | font-weight: normal; 57 | } 58 | 59 | .hljs-class .hljs-title, 60 | .hljs-type, 61 | .vhdl .hljs-literal, 62 | .tex .hljs-command { 63 | color: #458; 64 | font-weight: bold; 65 | } 66 | 67 | .hljs-tag, 68 | .hljs-tag .hljs-title, 69 | .hljs-rules .hljs-property, 70 | .django .hljs-tag .hljs-keyword { 71 | color: #000080; 72 | font-weight: normal; 73 | } 74 | 75 | .hljs-attribute, 76 | .hljs-variable, 77 | .lisp .hljs-body { 78 | color: #008080; 79 | } 80 | 81 | .hljs-regexp { 82 | color: #009926; 83 | } 84 | 85 | .hljs-symbol, 86 | .ruby .hljs-symbol .hljs-string, 87 | .lisp .hljs-keyword, 88 | .clojure .hljs-keyword, 89 | .scheme .hljs-keyword, 90 | .tex .hljs-special, 91 | .hljs-prompt { 92 | color: #990073; 93 | } 94 | 95 | .hljs-built_in { 96 | color: #0086b3; 97 | } 98 | 99 | .hljs-preprocessor, 100 | .hljs-pragma, 101 | .hljs-pi, 102 | .hljs-doctype, 103 | .hljs-shebang, 104 | .hljs-cdata { 105 | color: #999; 106 | font-weight: bold; 107 | } 108 | 109 | .hljs-deletion { 110 | background: #fdd; 111 | } 112 | 113 | .hljs-addition { 114 | background: #dfd; 115 | } 116 | 117 | .diff .hljs-change { 118 | background: #0086b3; 119 | } 120 | 121 | .hljs-chunk { 122 | color: #aaa; 123 | } 124 | -------------------------------------------------------------------------------- /src/activationFunctions.js: -------------------------------------------------------------------------------- 1 | function logistic(val) { 2 | return 1 / (1 + Math.exp(-val)); 3 | } 4 | 5 | function expELU(val, param) { 6 | return val < 0 ? param * (Math.exp(val) - 1) : val; 7 | } 8 | 9 | function softExponential(val, param) { 10 | if (param < 0) { 11 | return -Math.log(1 - param * (val + param)) / param; 12 | } 13 | if (param > 0) { 14 | return ((Math.exp(param * val) - 1) / param) + param; 15 | } 16 | return val; 17 | } 18 | 19 | function softExponentialPrime(val, param) { 20 | if (param < 0) { 21 | return 1 / (1 - param * (param + val)); 22 | } else { 23 | return Math.exp(param * val); 24 | } 25 | } 26 | 27 | const ACTIVATION_FUNCTIONS = { 28 | tanh: { 29 | activation: Math.tanh, 30 | derivate: (val) => 1 - (val * val) 31 | }, 32 | identity: { 33 | activation: (val) => val, 34 | derivate: () => 1 35 | }, 36 | logistic: { 37 | activation: logistic, 38 | derivate: (val) => logistic(val) * (1 - logistic(val)) 39 | }, 40 | arctan: { 41 | activation: Math.atan, 42 | derivate: (val) => 1 / (val * val + 1) 43 | }, 44 | softsign: { 45 | activation: (val) => val / (1 + Math.abs(val)), 46 | derivate: (val) => 1 / ((1 + Math.abs(val)) * (1 + Math.abs(val))) 47 | }, 48 | relu: { 49 | activation: (val) => (val < 0 ? 0 : val), 50 | derivate: (val) => (val < 0 ? 0 : 1) 51 | }, 52 | softplus: { 53 | activation: (val) => Math.log(1 + Math.exp(val)), 54 | derivate: (val) => 1 / (1 + Math.exp(-val)) 55 | }, 56 | bent: { 57 | activation: (val) => ((Math.sqrt(val * val + 1) - 1) / 2) + val, 58 | derivate: (val) => (val / (2 * Math.sqrt(val * val + 1))) + 1 59 | }, 60 | sinusoid: { 61 | activation: Math.sin, 62 | derivate: Math.cos 63 | }, 64 | sinc: { 65 | activation: (val) => (val === 0 ? 1 : Math.sin(val) / val), 66 | derivate: (val) => (val === 0 ? 0 : (Math.cos(val) / val) - (Math.sin(val) / (val * val))) 67 | }, 68 | gaussian: { 69 | activation: (val) => Math.exp(-(val * val)), 70 | derivate: (val) => -2 * val * Math.exp(-(val * val)) 71 | }, 72 | 'parametric-relu': { 73 | activation: (val, param) => (val < 0 ? param * val : val), 74 | derivate: (val, param) => (val < 0 ? param : 1) 75 | }, 76 | 'exponential-elu': { 77 | activation: expELU, 78 | derivate: (val, param) => (val < 0 ? expELU(val, param) + param : 1) 79 | }, 80 | 'soft-exponential': { 81 | activation: softExponential, 82 | derivate: softExponentialPrime 83 | } 84 | }; 85 | 86 | export default ACTIVATION_FUNCTIONS; 87 | -------------------------------------------------------------------------------- /docs/assets/style.css: -------------------------------------------------------------------------------- 1 | .documentation { 2 | font-family: Helvetica, sans-serif; 3 | color: #666; 4 | line-height: 1.5; 5 | background: #f5f5f5; 6 | } 7 | 8 | .black { 9 | color: #666; 10 | } 11 | 12 | .bg-white { 13 | background-color: #fff; 14 | } 15 | 16 | h4 { 17 | margin: 20px 0 10px 0; 18 | } 19 | 20 | .documentation h3 { 21 | color: #000; 22 | } 23 | 24 | .border-bottom { 25 | border-color: #ddd; 26 | } 27 | 28 | a { 29 | color: #1184CE; 30 | text-decoration: none; 31 | } 32 | 33 | .documentation a[href]:hover { 34 | text-decoration: underline; 35 | } 36 | 37 | a:hover { 38 | cursor: pointer; 39 | } 40 | 41 | .py1-ul li { 42 | padding: 5px 0; 43 | } 44 | 45 | .max-height-100 { 46 | max-height: 100%; 47 | } 48 | 49 | .height-viewport-100 { 50 | height: 100vh; 51 | } 52 | 53 | section:target h3 { 54 | font-weight:700; 55 | } 56 | 57 | .documentation td, 58 | .documentation th { 59 | padding: .25rem .25rem; 60 | } 61 | 62 | h1:hover .anchorjs-link, 63 | h2:hover .anchorjs-link, 64 | h3:hover .anchorjs-link, 65 | h4:hover .anchorjs-link { 66 | opacity: 1; 67 | } 68 | 69 | .fix-3 { 70 | width: 25%; 71 | max-width: 244px; 72 | } 73 | 74 | .fix-3 { 75 | width: 25%; 76 | max-width: 244px; 77 | } 78 | 79 | @media (min-width: 52em) { 80 | .fix-margin-3 { 81 | margin-left: 25%; 82 | } 83 | } 84 | 85 | .pre, pre, code, .code { 86 | font-family: Source Code Pro,Menlo,Consolas,Liberation Mono,monospace; 87 | font-size: 14px; 88 | } 89 | 90 | .fill-light { 91 | background: #F9F9F9; 92 | } 93 | 94 | .width2 { 95 | width: 1rem; 96 | } 97 | 98 | .input { 99 | font-family: inherit; 100 | display: block; 101 | width: 100%; 102 | height: 2rem; 103 | padding: .5rem; 104 | margin-bottom: 1rem; 105 | border: 1px solid #ccc; 106 | font-size: .875rem; 107 | border-radius: 3px; 108 | box-sizing: border-box; 109 | } 110 | 111 | table { 112 | border-collapse: collapse; 113 | } 114 | 115 | .prose table th, 116 | .prose table td { 117 | text-align: left; 118 | padding:8px; 119 | border:1px solid #ddd; 120 | } 121 | 122 | .prose table th:nth-child(1) { border-right: none; } 123 | .prose table th:nth-child(2) { border-left: none; } 124 | 125 | .prose table { 126 | border:1px solid #ddd; 127 | } 128 | 129 | .prose-big { 130 | font-size: 18px; 131 | line-height: 30px; 132 | } 133 | 134 | .quiet { 135 | opacity: 0.7; 136 | } 137 | 138 | .minishadow { 139 | box-shadow: 2px 2px 10px #f3f3f3; 140 | } 141 | -------------------------------------------------------------------------------- /src/Layer.js: -------------------------------------------------------------------------------- 1 | import { Matrix } from 'ml-matrix'; 2 | 3 | import ACTIVATION_FUNCTIONS from './activationFunctions'; 4 | 5 | export class Layer { 6 | /** 7 | * @private 8 | * Create a new layer with the given options 9 | * @param {object} options 10 | * @param {number} [options.inputSize] - Number of conections that enter the neurons. 11 | * @param {number} [options.outputSize] - Number of conections that leave the neurons. 12 | * @param {number} [options.regularization] - Regularization parameter. 13 | * @param {number} [options.epsilon] - Learning rate parameter. 14 | * @param {string} [options.activation] - Activation function parameter from the FeedForwardNeuralNetwork class. 15 | * @param {number} [options.activationParam] - Activation parameter if needed. 16 | */ 17 | constructor(options) { 18 | this.inputSize = options.inputSize; 19 | this.outputSize = options.outputSize; 20 | this.regularization = options.regularization; 21 | this.epsilon = options.epsilon; 22 | this.activation = options.activation; 23 | this.activationParam = options.activationParam; 24 | 25 | var selectedFunction = ACTIVATION_FUNCTIONS[options.activation]; 26 | var params = selectedFunction.activation.length; 27 | 28 | var actFunction = params > 1 ? (val) => selectedFunction.activation(val, options.activationParam) : selectedFunction.activation; 29 | var derFunction = params > 1 ? (val) => selectedFunction.derivate(val, options.activationParam) : selectedFunction.derivate; 30 | 31 | this.activationFunction = function (i, j) { 32 | this.set(i, j, actFunction(this.get(i, j))); 33 | }; 34 | this.derivate = function (i, j) { 35 | this.set(i, j, derFunction(this.get(i, j))); 36 | }; 37 | 38 | if (options.model) { 39 | // load model 40 | this.W = Matrix.checkMatrix(options.W); 41 | this.b = Matrix.checkMatrix(options.b); 42 | } else { 43 | // default constructor 44 | this.W = Matrix.rand(this.inputSize, this.outputSize); 45 | this.b = Matrix.zeros(1, this.outputSize); 46 | 47 | this.W.apply(function (i, j) { 48 | this.set(i, j, this.get(i, j) / Math.sqrt(options.inputSize)); 49 | }); 50 | } 51 | } 52 | 53 | /** 54 | * @private 55 | * propagate the given input through the current layer. 56 | * @param {Matrix} X - input. 57 | * @return {Matrix} output at the current layer. 58 | */ 59 | forward(X) { 60 | var z = X.mmul(this.W).addRowVector(this.b); 61 | z.apply(this.activationFunction); 62 | this.a = z.clone(); 63 | return z; 64 | } 65 | 66 | /** 67 | * @private 68 | * apply backpropagation algorithm at the current layer 69 | * @param {Matrix} delta - delta values estimated at the following layer. 70 | * @param {Matrix} a - 'a' values from the following layer. 71 | * @return {Matrix} the new delta values for the next layer. 72 | */ 73 | backpropagation(delta, a) { 74 | this.dW = a.transpose().mmul(delta); 75 | this.db = Matrix.rowVector(delta.sum('column')); 76 | 77 | var aCopy = a.clone(); 78 | return delta.mmul(this.W.transpose()).mul(aCopy.apply(this.derivate)); 79 | } 80 | 81 | /** 82 | * @private 83 | * Function that updates the weights at the current layer with the derivatives. 84 | */ 85 | update() { 86 | this.dW.add(this.W.clone().mul(this.regularization)); 87 | this.W.add(this.dW.mul(-this.epsilon)); 88 | this.b.add(this.db.mul(-this.epsilon)); 89 | } 90 | 91 | /** 92 | * @private 93 | * Export the current layer to JSON. 94 | * @return {object} model 95 | */ 96 | toJSON() { 97 | return { 98 | model: 'Layer', 99 | inputSize: this.inputSize, 100 | outputSize: this.outputSize, 101 | regularization: this.regularization, 102 | epsilon: this.epsilon, 103 | activation: this.activation, 104 | W: this.W, 105 | b: this.b 106 | }; 107 | } 108 | 109 | /** 110 | * @private 111 | * Creates a new Layer with the given model. 112 | * @param {object} model 113 | * @return {Layer} 114 | */ 115 | static load(model) { 116 | if (model.model !== 'Layer') { 117 | throw new RangeError('the current model is not a Layer model'); 118 | } 119 | return new Layer(model); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /docs/assets/fonts/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | 5 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /docs/assets/site.js: -------------------------------------------------------------------------------- 1 | /* global anchors */ 2 | 3 | // add anchor links to headers 4 | anchors.options.placement = 'left'; 5 | anchors.add('h3'); 6 | 7 | // Filter UI 8 | var tocElements = document.getElementById('toc').getElementsByTagName('li'); 9 | 10 | document.getElementById('filter-input').addEventListener('keyup', function(e) { 11 | var i, element, children; 12 | 13 | // enter key 14 | if (e.keyCode === 13) { 15 | // go to the first displayed item in the toc 16 | for (i = 0; i < tocElements.length; i++) { 17 | element = tocElements[i]; 18 | if (!element.classList.contains('display-none')) { 19 | location.replace(element.firstChild.href); 20 | return e.preventDefault(); 21 | } 22 | } 23 | } 24 | 25 | var match = function() { 26 | return true; 27 | }; 28 | 29 | var value = this.value.toLowerCase(); 30 | 31 | if (!value.match(/^\s*$/)) { 32 | match = function(element) { 33 | var html = element.firstChild.innerHTML; 34 | return html && html.toLowerCase().indexOf(value) !== -1; 35 | }; 36 | } 37 | 38 | for (i = 0; i < tocElements.length; i++) { 39 | element = tocElements[i]; 40 | children = Array.from(element.getElementsByTagName('li')); 41 | if (match(element) || children.some(match)) { 42 | element.classList.remove('display-none'); 43 | } else { 44 | element.classList.add('display-none'); 45 | } 46 | } 47 | }); 48 | 49 | var items = document.getElementsByClassName('toggle-sibling'); 50 | for (var j = 0; j < items.length; j++) { 51 | items[j].addEventListener('click', toggleSibling); 52 | } 53 | 54 | function toggleSibling() { 55 | var stepSibling = this.parentNode.getElementsByClassName('toggle-target')[0]; 56 | var icon = this.getElementsByClassName('icon')[0]; 57 | var klass = 'display-none'; 58 | if (stepSibling.classList.contains(klass)) { 59 | stepSibling.classList.remove(klass); 60 | icon.innerHTML = '▾'; 61 | } else { 62 | stepSibling.classList.add(klass); 63 | icon.innerHTML = '▸'; 64 | } 65 | } 66 | 67 | function showHashTarget(targetId) { 68 | if (targetId) { 69 | var hashTarget = document.getElementById(targetId); 70 | // new target is hidden 71 | if ( 72 | hashTarget && 73 | hashTarget.offsetHeight === 0 && 74 | hashTarget.parentNode.parentNode.classList.contains('display-none') 75 | ) { 76 | hashTarget.parentNode.parentNode.classList.remove('display-none'); 77 | } 78 | } 79 | } 80 | 81 | function scrollIntoView(targetId) { 82 | // Only scroll to element if we don't have a stored scroll position. 83 | if (targetId && !history.state) { 84 | var hashTarget = document.getElementById(targetId); 85 | if (hashTarget) { 86 | hashTarget.scrollIntoView(); 87 | } 88 | } 89 | } 90 | 91 | function gotoCurrentTarget() { 92 | showHashTarget(location.hash.substring(1)); 93 | scrollIntoView(location.hash.substring(1)); 94 | } 95 | 96 | window.addEventListener('hashchange', gotoCurrentTarget); 97 | gotoCurrentTarget(); 98 | 99 | var toclinks = document.getElementsByClassName('pre-open'); 100 | for (var k = 0; k < toclinks.length; k++) { 101 | toclinks[k].addEventListener('mousedown', preOpen, false); 102 | } 103 | 104 | function preOpen() { 105 | showHashTarget(this.hash.substring(1)); 106 | } 107 | 108 | var split_left = document.querySelector('#split-left'); 109 | var split_right = document.querySelector('#split-right'); 110 | var split_parent = split_left.parentNode; 111 | var cw_with_sb = split_left.clientWidth; 112 | split_left.style.overflow = 'hidden'; 113 | var cw_without_sb = split_left.clientWidth; 114 | split_left.style.overflow = ''; 115 | 116 | Split(['#split-left', '#split-right'], { 117 | elementStyle: function(dimension, size, gutterSize) { 118 | return { 119 | 'flex-basis': 'calc(' + size + '% - ' + gutterSize + 'px)' 120 | }; 121 | }, 122 | gutterStyle: function(dimension, gutterSize) { 123 | return { 124 | 'flex-basis': gutterSize + 'px' 125 | }; 126 | }, 127 | gutterSize: 20, 128 | sizes: [33, 67] 129 | }); 130 | 131 | // Chrome doesn't remember scroll position properly so do it ourselves. 132 | // Also works on Firefox and Edge. 133 | 134 | function updateState() { 135 | history.replaceState( 136 | { 137 | left_top: split_left.scrollTop, 138 | right_top: split_right.scrollTop 139 | }, 140 | document.title 141 | ); 142 | } 143 | 144 | function loadState(ev) { 145 | if (ev) { 146 | // Edge doesn't replace change history.state on popstate. 147 | history.replaceState(ev.state, document.title); 148 | } 149 | if (history.state) { 150 | split_left.scrollTop = history.state.left_top; 151 | split_right.scrollTop = history.state.right_top; 152 | } 153 | } 154 | 155 | window.addEventListener('load', function() { 156 | // Restore after Firefox scrolls to hash. 157 | setTimeout(function() { 158 | loadState(); 159 | // Update with initial scroll position. 160 | updateState(); 161 | // Update scroll positions only after we've loaded because Firefox 162 | // emits an initial scroll event with 0. 163 | split_left.addEventListener('scroll', updateState); 164 | split_right.addEventListener('scroll', updateState); 165 | }, 1); 166 | }); 167 | 168 | window.addEventListener('popstate', loadState); 169 | -------------------------------------------------------------------------------- /src/__tests__/test.js: -------------------------------------------------------------------------------- 1 | 2 | import { Matrix } from 'ml-matrix'; 3 | 4 | import ACTIVATION_FUNCTIONS from '../activationFunctions'; 5 | import FeedforwardNeuralNetwork from '../FeedForwardNeuralNetwork'; 6 | 7 | describe('Feedforward Neural Networks', function () { 8 | var functions = Object.keys(ACTIVATION_FUNCTIONS); 9 | 10 | it('Training the neural network with XOR operator', function () { 11 | var trainingSet = new Matrix([[0, 0], [0, 1], [1, 0], [1, 1]]); 12 | var predictions = [false, true, true, false]; 13 | 14 | for (var i = 0; i < functions.length; ++i) { 15 | var options = { 16 | hiddenLayers: [4], 17 | iterations: 500, 18 | learningRate: 0.3, 19 | activation: functions[i] 20 | }; 21 | var xorNN = new FeedforwardNeuralNetwork(options); 22 | 23 | xorNN.train(trainingSet, predictions); 24 | var results = xorNN.predict(trainingSet); 25 | 26 | expect(results[0]).toBe(predictions[0]); 27 | expect(results[1]).toBe(predictions[1]); 28 | expect(results[2]).toBe(predictions[2]); 29 | expect(results[3]).toBe(predictions[3]); 30 | } 31 | }); 32 | 33 | it('Training the neural network with AND operator', function () { 34 | var trainingSet = [[0, 0], [0, 1], [1, 0], [1, 1]]; 35 | var predictions = [[1, 0], [1, 0], [1, 0], [0, 1]]; 36 | 37 | for (var i = 0; i < functions.length; ++i) { 38 | var options = { 39 | hiddenLayers: [3], 40 | iterations: 500, 41 | learningRate: 0.3, 42 | activation: functions[i] 43 | }; 44 | var andNN = new FeedforwardNeuralNetwork(options); 45 | andNN.train(trainingSet, predictions); 46 | 47 | var results = andNN.predict(trainingSet); 48 | 49 | expect(results[0][0]).toBeGreaterThan(results[0][1]); 50 | expect(results[1][0]).toBeGreaterThan(results[1][1]); 51 | expect(results[2][0]).toBeGreaterThan(results[2][1]); 52 | expect(results[3][0]).toBeLessThan(results[3][1]); 53 | } 54 | }); 55 | 56 | it('Export and import', function () { 57 | var trainingSet = [[0, 0], [0, 1], [1, 0], [1, 1]]; 58 | var predictions = [0, 1, 1, 1]; 59 | 60 | for (var i = 0; i < functions.length; ++i) { 61 | var options = { 62 | hiddenLayers: [4], 63 | iterations: 500, 64 | learningRate: 0.3, 65 | activation: functions[i] 66 | }; 67 | var orNN = new FeedforwardNeuralNetwork(options); 68 | orNN.train(trainingSet, predictions); 69 | 70 | var model = JSON.parse(JSON.stringify(orNN)); 71 | var networkNN = FeedforwardNeuralNetwork.load(model); 72 | 73 | var results = networkNN.predict(trainingSet); 74 | 75 | expect(results[0]).toBeCloseTo(predictions[0], 2); 76 | expect(results[1]).toBeCloseTo(predictions[1], 2); 77 | expect(results[2]).toBeCloseTo(predictions[2], 2); 78 | expect(results[3]).toBeCloseTo(predictions[3], 2); 79 | } 80 | }); 81 | 82 | it('Multiclass clasification', function () { 83 | var trainingSet = [[0, 0], [0, 1], [1, 0], [1, 1]]; 84 | var predictions = [2, 0, 1, 0]; 85 | 86 | for (var i = 0; i < functions.length; ++i) { 87 | var options = { 88 | hiddenLayers: [4], 89 | iterations: 300, 90 | learningRate: 0.5, 91 | activation: functions[i] 92 | }; 93 | var nn = new FeedforwardNeuralNetwork(options); 94 | nn.train(trainingSet, predictions); 95 | 96 | var result = nn.predict(trainingSet); 97 | 98 | expect(result[0]).toBeCloseTo(2, 1); 99 | expect(result[1]).toBeCloseTo(0, 1); 100 | expect(result[2]).toBeCloseTo(1, 1); 101 | expect(result[3]).toBeCloseTo(0, 1); 102 | } 103 | }); 104 | 105 | it('Big case', function () { 106 | var trainingSet = [ 107 | [1, 1], [1, 2], [2, 1], [2, 2], [3, 1], [1, 3], [1, 4], [4, 1], 108 | [6, 1], [6, 2], [6, 3], [6, 4], [6, 5], [5, 5], [4, 5], [3, 5] 109 | ]; 110 | var predictions = [ 111 | [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], 112 | [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1] 113 | ]; 114 | for (var i = 0; i < functions.length; ++i) { 115 | var options = { 116 | hiddenLayers: [20], 117 | iterations: 1000, 118 | learningRate: 0.01, 119 | activation: functions[i] 120 | }; 121 | var nn = new FeedforwardNeuralNetwork(options); 122 | nn.train(trainingSet, predictions); 123 | 124 | var result = nn.predict([[5, 4]]); 125 | 126 | expect(result[0][0]).toBeLessThan(result[0][1]); 127 | } 128 | }); 129 | 130 | it('Big case - many predictions', function () { 131 | var trainingSet = [ 132 | [1, 1], [1, 2], [2, 1], [2, 2], [3, 1], [1, 3], [1, 4], [4, 1], 133 | [6, 1], [6, 2], [6, 3], [6, 4], [6, 5], [5, 5], [4, 5], [3, 5] 134 | ]; 135 | var predictions = [ 136 | [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], 137 | [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1], [0, 1] 138 | ]; 139 | for (var i = 0; i < functions.length; ++i) { 140 | var options = { 141 | hiddenLayers: [20], 142 | iterations: 1000, 143 | learningRate: 0.01, 144 | activation: functions[i] 145 | }; 146 | var nn = new FeedforwardNeuralNetwork(options); 147 | nn.train(trainingSet, predictions); 148 | 149 | var result = nn.predict([[5, 4], [4, 2], [5, 3]]); 150 | expect(result[0][0]).toBeLessThan(result[0][1]); 151 | expect(result[1][1]).toBeLessThan(result[1][0]); 152 | expect(result[2][0]).toBeLessThan(result[2][1]); 153 | } 154 | }); 155 | 156 | it.skip('Big test case 2', function () { 157 | // see https://gist.github.com/jajoe/cb53d7b1378a76cc6896e660f83b50d2, this test case should work 158 | var X = [[0, 255, 255, 255, 0], [255, 0, 0, 0, 255], [255, 255, 0, 0, 0], [255, 0, 0, 0, 0], [0, 255, 255, 255, 255], [0, 255, 255, 0, 0], [0, 255, 0, 0, 255], [0, 0, 255, 0, 255], [255, 255, 0, 0, 255], [255, 0, 0, 0, 255], [255, 255, 0, 255, 0], [0, 0, 0, 255, 0], [255, 0, 0, 255, 0], [255, 0, 0, 255, 255], [0, 0, 255, 0, 255]]; 159 | var y = [[0, 1], [1, 0], [1, 1], [1, 1], [1, 1], [0, 1], [0, 0], [0, 0], [1, 0], [1, 0], [0, 0], [0, 1], [0, 0], [1, 0], [0, 0]]; 160 | var Xtest = [[255, 0, 255, 255, 255], [0, 255, 0, 0, 0], [255, 0, 255, 255, 0], [0, 0, 255, 255, 255]]; 161 | var ytest = [[1, 0], [0, 1], [0, 0], [1, 1]]; 162 | var options = { 163 | hiddenLayers: [100], 164 | iterations: 10000, 165 | learningRate: 0.001, 166 | activation: 'logistic' 167 | }; 168 | var nn = new FeedforwardNeuralNetwork(options); 169 | nn.train(X, y); 170 | 171 | var result = nn.predict(Xtest); 172 | for (let i = 0; i < ytest.length; i++) { 173 | for (let j = 0; j < ytest[0].length; j++) { 174 | expect(result[i][j]).toBeCloseTo(ytest[i][j], 1); 175 | } 176 | } 177 | }); 178 | }); 179 | -------------------------------------------------------------------------------- /src/FeedForwardNeuralNetwork.js: -------------------------------------------------------------------------------- 1 | import { Matrix } from 'ml-matrix'; 2 | 3 | import { Layer } from './Layer'; 4 | import { OutputLayer } from './OutputLayer'; 5 | import ACTIVATION_FUNCTIONS from './activationFunctions'; 6 | 7 | export default class FeedForwardNeuralNetworks { 8 | /** 9 | * Create a new Feedforward neural network model. 10 | * @class FeedForwardNeuralNetworks 11 | * @param {object} [options] 12 | * @param {Array} [options.hiddenLayers=[10]] - Array that contains the sizes of the hidden layers. 13 | * @param {number} [options.iterations=50] - Number of iterations at the training step. 14 | * @param {number} [options.learningRate=0.01] - Learning rate of the neural net (also known as epsilon). 15 | * @param {number} [options.regularization=0.01] - Regularization parameter af the neural net. 16 | * @param {string} [options.activation='tanh'] - activation function to be used. (options: 'tanh'(default), 17 | * 'identity', 'logistic', 'arctan', 'softsign', 'relu', 'softplus', 'bent', 'sinusoid', 'sinc', 'gaussian'). 18 | * (single-parametric options: 'parametric-relu', 'exponential-relu', 'soft-exponential'). 19 | * @param {number} [options.activationParam=1] - if the selected activation function needs a parameter. 20 | */ 21 | constructor(options) { 22 | options = options || {}; 23 | if (options.model) { 24 | // load network 25 | this.hiddenLayers = options.hiddenLayers; 26 | this.iterations = options.iterations; 27 | this.learningRate = options.learningRate; 28 | this.regularization = options.regularization; 29 | this.dicts = options.dicts; 30 | this.activation = options.activation; 31 | this.activationParam = options.activationParam; 32 | this.model = new Array(options.layers.length); 33 | 34 | for (var i = 0; i < this.model.length - 1; ++i) { 35 | this.model[i] = Layer.load(options.layers[i]); 36 | } 37 | this.model[this.model.length - 1] = OutputLayer.load(options.layers[this.model.length - 1]); 38 | } else { 39 | // default constructor 40 | this.hiddenLayers = options.hiddenLayers || [10]; 41 | this.iterations = options.iterations || 50; 42 | 43 | this.learningRate = options.learningRate || 0.01; 44 | this.regularization = options.regularization || 0.01; 45 | 46 | this.activation = options.activation || 'tanh'; 47 | this.activationParam = options.activationParam || 1; 48 | if (!(this.activation in Object.keys(ACTIVATION_FUNCTIONS))) { 49 | this.activation = 'tanh'; 50 | } 51 | } 52 | } 53 | 54 | /** 55 | * @private 56 | * Function that build and initialize the neural net. 57 | * @param {number} inputSize - total of features to fit. 58 | * @param {number} outputSize - total of labels of the prediction set. 59 | */ 60 | buildNetwork(inputSize, outputSize) { 61 | var size = 2 + (this.hiddenLayers.length - 1); 62 | this.model = new Array(size); 63 | 64 | // input layer 65 | this.model[0] = new Layer({ 66 | inputSize: inputSize, 67 | outputSize: this.hiddenLayers[0], 68 | activation: this.activation, 69 | activationParam: this.activationParam, 70 | regularization: this.regularization, 71 | epsilon: this.learningRate 72 | }); 73 | 74 | // hidden layers 75 | for (var i = 1; i < this.hiddenLayers.length; ++i) { 76 | this.model[i] = new Layer({ 77 | inputSize: this.hiddenLayers[i - 1], 78 | outputSize: this.hiddenLayers[i], 79 | activation: this.activation, 80 | activationParam: this.activationParam, 81 | regularization: this.regularization, 82 | epsilon: this.learningRate 83 | }); 84 | } 85 | 86 | // output layer 87 | this.model[size - 1] = new OutputLayer({ 88 | inputSize: this.hiddenLayers[this.hiddenLayers.length - 1], 89 | outputSize: outputSize, 90 | activation: this.activation, 91 | activationParam: this.activationParam, 92 | regularization: this.regularization, 93 | epsilon: this.learningRate 94 | }); 95 | } 96 | 97 | /** 98 | * Train the neural net with the given features and labels. 99 | * @param {Matrix|Array} features 100 | * @param {Matrix|Array} labels 101 | */ 102 | train(features, labels) { 103 | features = Matrix.checkMatrix(features); 104 | this.dicts = dictOutputs(labels); 105 | 106 | var inputSize = features.columns; 107 | var outputSize = Object.keys(this.dicts.inputs).length; 108 | 109 | if (!this.model) { 110 | this.buildNetwork(inputSize, outputSize); 111 | } 112 | 113 | for (var i = 0; i < this.iterations; ++i) { 114 | var probabilities = this.propagate(features); 115 | this.backpropagation(features, labels, probabilities); 116 | } 117 | } 118 | 119 | /** 120 | * @private 121 | * Propagate the input(training set) and retrives the probabilities of each class. 122 | * @param {Matrix} X 123 | * @return {Matrix} probabilities of each class. 124 | */ 125 | propagate(X) { 126 | var input = X; 127 | for (var i = 0; i < this.model.length; ++i) { 128 | input = this.model[i].forward(input); 129 | } 130 | 131 | // get probabilities 132 | return input.divColumnVector(input.sum('row')); 133 | } 134 | 135 | /** 136 | * @private 137 | * Function that applies the backpropagation algorithm on each layer of the network 138 | * in order to fit the features and labels. 139 | * @param {Matrix} features 140 | * @param {Array} labels 141 | * @param {Matrix} probabilities - probabilities of each class of the feature set. 142 | */ 143 | backpropagation(features, labels, probabilities) { 144 | for (var i = 0; i < probabilities.rows; ++i) { 145 | probabilities.set(i, this.dicts.inputs[labels[i]], probabilities.get(i, this.dicts.inputs[labels[i]]) - 1); 146 | } 147 | 148 | // remember, the last delta doesn't matter 149 | var delta = probabilities; 150 | for (i = this.model.length - 1; i >= 0; --i) { 151 | var a = i > 0 ? this.model[i - 1].a : features; 152 | delta = this.model[i].backpropagation(delta, a); 153 | } 154 | 155 | for (i = 0; i < this.model.length; ++i) { 156 | this.model[i].update(); 157 | } 158 | } 159 | 160 | /** 161 | * Predict the output given the feature set. 162 | * @param {Array|Matrix} features 163 | * @return {Array} 164 | */ 165 | predict(features) { 166 | features = Matrix.checkMatrix(features); 167 | var outputs = new Array(features.rows); 168 | var probabilities = this.propagate(features); 169 | for (var i = 0; i < features.rows; ++i) { 170 | outputs[i] = this.dicts.outputs[probabilities.maxRowIndex(i)[1]]; 171 | } 172 | 173 | return outputs; 174 | } 175 | 176 | /** 177 | * Export the current model to JSON. 178 | * @return {object} model 179 | */ 180 | toJSON() { 181 | var model = { 182 | model: 'FNN', 183 | hiddenLayers: this.hiddenLayers, 184 | iterations: this.iterations, 185 | learningRate: this.learningRate, 186 | regularization: this.regularization, 187 | activation: this.activation, 188 | activationParam: this.activationParam, 189 | dicts: this.dicts, 190 | layers: new Array(this.model.length) 191 | }; 192 | 193 | for (var i = 0; i < this.model.length; ++i) { 194 | model.layers[i] = this.model[i].toJSON(); 195 | } 196 | 197 | return model; 198 | } 199 | 200 | /** 201 | * Load a Feedforward Neural Network with the current model. 202 | * @param {object} model 203 | * @return {FeedForwardNeuralNetworks} 204 | */ 205 | static load(model) { 206 | if (model.model !== 'FNN') { 207 | throw new RangeError('the current model is not a feed forward network'); 208 | } 209 | 210 | return new FeedForwardNeuralNetworks(model); 211 | } 212 | } 213 | 214 | /** 215 | * @private 216 | * Method that given an array of labels(predictions), returns two dictionaries, one to transform from labels to 217 | * numbers and other in the reverse way 218 | * @param {Array} array 219 | * @return {object} 220 | */ 221 | function dictOutputs(array) { 222 | var inputs = {}; 223 | var outputs = {}; 224 | var index = 0; 225 | for (var i = 0; i < array.length; i += 1) { 226 | if (inputs[array[i]] === undefined) { 227 | inputs[array[i]] = index; 228 | outputs[index] = array[i]; 229 | index++; 230 | } 231 | } 232 | 233 | return { 234 | inputs: inputs, 235 | outputs: outputs 236 | }; 237 | } 238 | -------------------------------------------------------------------------------- /docs/assets/bass.css: -------------------------------------------------------------------------------- 1 | /*! Basscss | http://basscss.com | MIT License */ 2 | 3 | .h1{ font-size: 2rem } 4 | .h2{ font-size: 1.5rem } 5 | .h3{ font-size: 1.25rem } 6 | .h4{ font-size: 1rem } 7 | .h5{ font-size: .875rem } 8 | .h6{ font-size: .75rem } 9 | 10 | .font-family-inherit{ font-family:inherit } 11 | .font-size-inherit{ font-size:inherit } 12 | .text-decoration-none{ text-decoration:none } 13 | 14 | .bold{ font-weight: bold; font-weight: bold } 15 | .regular{ font-weight:normal } 16 | .italic{ font-style:italic } 17 | .caps{ text-transform:uppercase; letter-spacing: .2em; } 18 | 19 | .left-align{ text-align:left } 20 | .center{ text-align:center } 21 | .right-align{ text-align:right } 22 | .justify{ text-align:justify } 23 | 24 | .nowrap{ white-space:nowrap } 25 | .break-word{ word-wrap:break-word } 26 | 27 | .line-height-1{ line-height: 1 } 28 | .line-height-2{ line-height: 1.125 } 29 | .line-height-3{ line-height: 1.25 } 30 | .line-height-4{ line-height: 1.5 } 31 | 32 | .list-style-none{ list-style:none } 33 | .underline{ text-decoration:underline } 34 | 35 | .truncate{ 36 | max-width:100%; 37 | overflow:hidden; 38 | text-overflow:ellipsis; 39 | white-space:nowrap; 40 | } 41 | 42 | .list-reset{ 43 | list-style:none; 44 | padding-left:0; 45 | } 46 | 47 | .inline{ display:inline } 48 | .block{ display:block } 49 | .inline-block{ display:inline-block } 50 | .table{ display:table } 51 | .table-cell{ display:table-cell } 52 | 53 | .overflow-hidden{ overflow:hidden } 54 | .overflow-scroll{ overflow:scroll } 55 | .overflow-auto{ overflow:auto } 56 | 57 | .clearfix:before, 58 | .clearfix:after{ 59 | content:" "; 60 | display:table 61 | } 62 | .clearfix:after{ clear:both } 63 | 64 | .left{ float:left } 65 | .right{ float:right } 66 | 67 | .fit{ max-width:100% } 68 | 69 | .max-width-1{ max-width: 24rem } 70 | .max-width-2{ max-width: 32rem } 71 | .max-width-3{ max-width: 48rem } 72 | .max-width-4{ max-width: 64rem } 73 | 74 | .border-box{ box-sizing:border-box } 75 | 76 | .align-baseline{ vertical-align:baseline } 77 | .align-top{ vertical-align:top } 78 | .align-middle{ vertical-align:middle } 79 | .align-bottom{ vertical-align:bottom } 80 | 81 | .m0{ margin:0 } 82 | .mt0{ margin-top:0 } 83 | .mr0{ margin-right:0 } 84 | .mb0{ margin-bottom:0 } 85 | .ml0{ margin-left:0 } 86 | .mx0{ margin-left:0; margin-right:0 } 87 | .my0{ margin-top:0; margin-bottom:0 } 88 | 89 | .m1{ margin: .5rem } 90 | .mt1{ margin-top: .5rem } 91 | .mr1{ margin-right: .5rem } 92 | .mb1{ margin-bottom: .5rem } 93 | .ml1{ margin-left: .5rem } 94 | .mx1{ margin-left: .5rem; margin-right: .5rem } 95 | .my1{ margin-top: .5rem; margin-bottom: .5rem } 96 | 97 | .m2{ margin: 1rem } 98 | .mt2{ margin-top: 1rem } 99 | .mr2{ margin-right: 1rem } 100 | .mb2{ margin-bottom: 1rem } 101 | .ml2{ margin-left: 1rem } 102 | .mx2{ margin-left: 1rem; margin-right: 1rem } 103 | .my2{ margin-top: 1rem; margin-bottom: 1rem } 104 | 105 | .m3{ margin: 2rem } 106 | .mt3{ margin-top: 2rem } 107 | .mr3{ margin-right: 2rem } 108 | .mb3{ margin-bottom: 2rem } 109 | .ml3{ margin-left: 2rem } 110 | .mx3{ margin-left: 2rem; margin-right: 2rem } 111 | .my3{ margin-top: 2rem; margin-bottom: 2rem } 112 | 113 | .m4{ margin: 4rem } 114 | .mt4{ margin-top: 4rem } 115 | .mr4{ margin-right: 4rem } 116 | .mb4{ margin-bottom: 4rem } 117 | .ml4{ margin-left: 4rem } 118 | .mx4{ margin-left: 4rem; margin-right: 4rem } 119 | .my4{ margin-top: 4rem; margin-bottom: 4rem } 120 | 121 | .mxn1{ margin-left: -.5rem; margin-right: -.5rem; } 122 | .mxn2{ margin-left: -1rem; margin-right: -1rem; } 123 | .mxn3{ margin-left: -2rem; margin-right: -2rem; } 124 | .mxn4{ margin-left: -4rem; margin-right: -4rem; } 125 | 126 | .ml-auto{ margin-left:auto } 127 | .mr-auto{ margin-right:auto } 128 | .mx-auto{ margin-left:auto; margin-right:auto; } 129 | 130 | .p0{ padding:0 } 131 | .pt0{ padding-top:0 } 132 | .pr0{ padding-right:0 } 133 | .pb0{ padding-bottom:0 } 134 | .pl0{ padding-left:0 } 135 | .px0{ padding-left:0; padding-right:0 } 136 | .py0{ padding-top:0; padding-bottom:0 } 137 | 138 | .p1{ padding: .5rem } 139 | .pt1{ padding-top: .5rem } 140 | .pr1{ padding-right: .5rem } 141 | .pb1{ padding-bottom: .5rem } 142 | .pl1{ padding-left: .5rem } 143 | .py1{ padding-top: .5rem; padding-bottom: .5rem } 144 | .px1{ padding-left: .5rem; padding-right: .5rem } 145 | 146 | .p2{ padding: 1rem } 147 | .pt2{ padding-top: 1rem } 148 | .pr2{ padding-right: 1rem } 149 | .pb2{ padding-bottom: 1rem } 150 | .pl2{ padding-left: 1rem } 151 | .py2{ padding-top: 1rem; padding-bottom: 1rem } 152 | .px2{ padding-left: 1rem; padding-right: 1rem } 153 | 154 | .p3{ padding: 2rem } 155 | .pt3{ padding-top: 2rem } 156 | .pr3{ padding-right: 2rem } 157 | .pb3{ padding-bottom: 2rem } 158 | .pl3{ padding-left: 2rem } 159 | .py3{ padding-top: 2rem; padding-bottom: 2rem } 160 | .px3{ padding-left: 2rem; padding-right: 2rem } 161 | 162 | .p4{ padding: 4rem } 163 | .pt4{ padding-top: 4rem } 164 | .pr4{ padding-right: 4rem } 165 | .pb4{ padding-bottom: 4rem } 166 | .pl4{ padding-left: 4rem } 167 | .py4{ padding-top: 4rem; padding-bottom: 4rem } 168 | .px4{ padding-left: 4rem; padding-right: 4rem } 169 | 170 | .col{ 171 | float:left; 172 | box-sizing:border-box; 173 | } 174 | 175 | .col-right{ 176 | float:right; 177 | box-sizing:border-box; 178 | } 179 | 180 | .col-1{ 181 | width:8.33333%; 182 | } 183 | 184 | .col-2{ 185 | width:16.66667%; 186 | } 187 | 188 | .col-3{ 189 | width:25%; 190 | } 191 | 192 | .col-4{ 193 | width:33.33333%; 194 | } 195 | 196 | .col-5{ 197 | width:41.66667%; 198 | } 199 | 200 | .col-6{ 201 | width:50%; 202 | } 203 | 204 | .col-7{ 205 | width:58.33333%; 206 | } 207 | 208 | .col-8{ 209 | width:66.66667%; 210 | } 211 | 212 | .col-9{ 213 | width:75%; 214 | } 215 | 216 | .col-10{ 217 | width:83.33333%; 218 | } 219 | 220 | .col-11{ 221 | width:91.66667%; 222 | } 223 | 224 | .col-12{ 225 | width:100%; 226 | } 227 | @media (min-width: 40em){ 228 | 229 | .sm-col{ 230 | float:left; 231 | box-sizing:border-box; 232 | } 233 | 234 | .sm-col-right{ 235 | float:right; 236 | box-sizing:border-box; 237 | } 238 | 239 | .sm-col-1{ 240 | width:8.33333%; 241 | } 242 | 243 | .sm-col-2{ 244 | width:16.66667%; 245 | } 246 | 247 | .sm-col-3{ 248 | width:25%; 249 | } 250 | 251 | .sm-col-4{ 252 | width:33.33333%; 253 | } 254 | 255 | .sm-col-5{ 256 | width:41.66667%; 257 | } 258 | 259 | .sm-col-6{ 260 | width:50%; 261 | } 262 | 263 | .sm-col-7{ 264 | width:58.33333%; 265 | } 266 | 267 | .sm-col-8{ 268 | width:66.66667%; 269 | } 270 | 271 | .sm-col-9{ 272 | width:75%; 273 | } 274 | 275 | .sm-col-10{ 276 | width:83.33333%; 277 | } 278 | 279 | .sm-col-11{ 280 | width:91.66667%; 281 | } 282 | 283 | .sm-col-12{ 284 | width:100%; 285 | } 286 | 287 | } 288 | @media (min-width: 52em){ 289 | 290 | .md-col{ 291 | float:left; 292 | box-sizing:border-box; 293 | } 294 | 295 | .md-col-right{ 296 | float:right; 297 | box-sizing:border-box; 298 | } 299 | 300 | .md-col-1{ 301 | width:8.33333%; 302 | } 303 | 304 | .md-col-2{ 305 | width:16.66667%; 306 | } 307 | 308 | .md-col-3{ 309 | width:25%; 310 | } 311 | 312 | .md-col-4{ 313 | width:33.33333%; 314 | } 315 | 316 | .md-col-5{ 317 | width:41.66667%; 318 | } 319 | 320 | .md-col-6{ 321 | width:50%; 322 | } 323 | 324 | .md-col-7{ 325 | width:58.33333%; 326 | } 327 | 328 | .md-col-8{ 329 | width:66.66667%; 330 | } 331 | 332 | .md-col-9{ 333 | width:75%; 334 | } 335 | 336 | .md-col-10{ 337 | width:83.33333%; 338 | } 339 | 340 | .md-col-11{ 341 | width:91.66667%; 342 | } 343 | 344 | .md-col-12{ 345 | width:100%; 346 | } 347 | 348 | } 349 | @media (min-width: 64em){ 350 | 351 | .lg-col{ 352 | float:left; 353 | box-sizing:border-box; 354 | } 355 | 356 | .lg-col-right{ 357 | float:right; 358 | box-sizing:border-box; 359 | } 360 | 361 | .lg-col-1{ 362 | width:8.33333%; 363 | } 364 | 365 | .lg-col-2{ 366 | width:16.66667%; 367 | } 368 | 369 | .lg-col-3{ 370 | width:25%; 371 | } 372 | 373 | .lg-col-4{ 374 | width:33.33333%; 375 | } 376 | 377 | .lg-col-5{ 378 | width:41.66667%; 379 | } 380 | 381 | .lg-col-6{ 382 | width:50%; 383 | } 384 | 385 | .lg-col-7{ 386 | width:58.33333%; 387 | } 388 | 389 | .lg-col-8{ 390 | width:66.66667%; 391 | } 392 | 393 | .lg-col-9{ 394 | width:75%; 395 | } 396 | 397 | .lg-col-10{ 398 | width:83.33333%; 399 | } 400 | 401 | .lg-col-11{ 402 | width:91.66667%; 403 | } 404 | 405 | .lg-col-12{ 406 | width:100%; 407 | } 408 | 409 | } 410 | .flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } 411 | 412 | @media (min-width: 40em){ 413 | .sm-flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } 414 | } 415 | 416 | @media (min-width: 52em){ 417 | .md-flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } 418 | } 419 | 420 | @media (min-width: 64em){ 421 | .lg-flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } 422 | } 423 | 424 | .flex-column{ -webkit-box-orient:vertical; -webkit-box-direction:normal; -webkit-flex-direction:column; -ms-flex-direction:column; flex-direction:column } 425 | .flex-wrap{ -webkit-flex-wrap:wrap; -ms-flex-wrap:wrap; flex-wrap:wrap } 426 | 427 | .items-start{ -webkit-box-align:start; -webkit-align-items:flex-start; -ms-flex-align:start; -ms-grid-row-align:flex-start; align-items:flex-start } 428 | .items-end{ -webkit-box-align:end; -webkit-align-items:flex-end; -ms-flex-align:end; -ms-grid-row-align:flex-end; align-items:flex-end } 429 | .items-center{ -webkit-box-align:center; -webkit-align-items:center; -ms-flex-align:center; -ms-grid-row-align:center; align-items:center } 430 | .items-baseline{ -webkit-box-align:baseline; -webkit-align-items:baseline; -ms-flex-align:baseline; -ms-grid-row-align:baseline; align-items:baseline } 431 | .items-stretch{ -webkit-box-align:stretch; -webkit-align-items:stretch; -ms-flex-align:stretch; -ms-grid-row-align:stretch; align-items:stretch } 432 | 433 | .self-start{ -webkit-align-self:flex-start; -ms-flex-item-align:start; align-self:flex-start } 434 | .self-end{ -webkit-align-self:flex-end; -ms-flex-item-align:end; align-self:flex-end } 435 | .self-center{ -webkit-align-self:center; -ms-flex-item-align:center; align-self:center } 436 | .self-baseline{ -webkit-align-self:baseline; -ms-flex-item-align:baseline; align-self:baseline } 437 | .self-stretch{ -webkit-align-self:stretch; -ms-flex-item-align:stretch; align-self:stretch } 438 | 439 | .justify-start{ -webkit-box-pack:start; -webkit-justify-content:flex-start; -ms-flex-pack:start; justify-content:flex-start } 440 | .justify-end{ -webkit-box-pack:end; -webkit-justify-content:flex-end; -ms-flex-pack:end; justify-content:flex-end } 441 | .justify-center{ -webkit-box-pack:center; -webkit-justify-content:center; -ms-flex-pack:center; justify-content:center } 442 | .justify-between{ -webkit-box-pack:justify; -webkit-justify-content:space-between; -ms-flex-pack:justify; justify-content:space-between } 443 | .justify-around{ -webkit-justify-content:space-around; -ms-flex-pack:distribute; justify-content:space-around } 444 | 445 | .content-start{ -webkit-align-content:flex-start; -ms-flex-line-pack:start; align-content:flex-start } 446 | .content-end{ -webkit-align-content:flex-end; -ms-flex-line-pack:end; align-content:flex-end } 447 | .content-center{ -webkit-align-content:center; -ms-flex-line-pack:center; align-content:center } 448 | .content-between{ -webkit-align-content:space-between; -ms-flex-line-pack:justify; align-content:space-between } 449 | .content-around{ -webkit-align-content:space-around; -ms-flex-line-pack:distribute; align-content:space-around } 450 | .content-stretch{ -webkit-align-content:stretch; -ms-flex-line-pack:stretch; align-content:stretch } 451 | .flex-auto{ 452 | -webkit-box-flex:1; 453 | -webkit-flex:1 1 auto; 454 | -ms-flex:1 1 auto; 455 | flex:1 1 auto; 456 | min-width:0; 457 | min-height:0; 458 | } 459 | .flex-none{ -webkit-box-flex:0; -webkit-flex:none; -ms-flex:none; flex:none } 460 | .fs0{ flex-shrink: 0 } 461 | 462 | .order-0{ -webkit-box-ordinal-group:1; -webkit-order:0; -ms-flex-order:0; order:0 } 463 | .order-1{ -webkit-box-ordinal-group:2; -webkit-order:1; -ms-flex-order:1; order:1 } 464 | .order-2{ -webkit-box-ordinal-group:3; -webkit-order:2; -ms-flex-order:2; order:2 } 465 | .order-3{ -webkit-box-ordinal-group:4; -webkit-order:3; -ms-flex-order:3; order:3 } 466 | .order-last{ -webkit-box-ordinal-group:100000; -webkit-order:99999; -ms-flex-order:99999; order:99999 } 467 | 468 | .relative{ position:relative } 469 | .absolute{ position:absolute } 470 | .fixed{ position:fixed } 471 | 472 | .top-0{ top:0 } 473 | .right-0{ right:0 } 474 | .bottom-0{ bottom:0 } 475 | .left-0{ left:0 } 476 | 477 | .z1{ z-index: 1 } 478 | .z2{ z-index: 2 } 479 | .z3{ z-index: 3 } 480 | .z4{ z-index: 4 } 481 | 482 | .border{ 483 | border-style:solid; 484 | border-width: 1px; 485 | } 486 | 487 | .border-top{ 488 | border-top-style:solid; 489 | border-top-width: 1px; 490 | } 491 | 492 | .border-right{ 493 | border-right-style:solid; 494 | border-right-width: 1px; 495 | } 496 | 497 | .border-bottom{ 498 | border-bottom-style:solid; 499 | border-bottom-width: 1px; 500 | } 501 | 502 | .border-left{ 503 | border-left-style:solid; 504 | border-left-width: 1px; 505 | } 506 | 507 | .border-none{ border:0 } 508 | 509 | .rounded{ border-radius: 3px } 510 | .circle{ border-radius:50% } 511 | 512 | .rounded-top{ border-radius: 3px 3px 0 0 } 513 | .rounded-right{ border-radius: 0 3px 3px 0 } 514 | .rounded-bottom{ border-radius: 0 0 3px 3px } 515 | .rounded-left{ border-radius: 3px 0 0 3px } 516 | 517 | .not-rounded{ border-radius:0 } 518 | 519 | .hide{ 520 | position:absolute !important; 521 | height:1px; 522 | width:1px; 523 | overflow:hidden; 524 | clip:rect(1px, 1px, 1px, 1px); 525 | } 526 | 527 | @media (max-width: 40em){ 528 | .xs-hide{ display:none !important } 529 | } 530 | 531 | @media (min-width: 40em) and (max-width: 52em){ 532 | .sm-hide{ display:none !important } 533 | } 534 | 535 | @media (min-width: 52em) and (max-width: 64em){ 536 | .md-hide{ display:none !important } 537 | } 538 | 539 | @media (min-width: 64em){ 540 | .lg-hide{ display:none !important } 541 | } 542 | 543 | .display-none{ display:none !important } 544 | 545 | -------------------------------------------------------------------------------- /docs/assets/anchor.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * AnchorJS - v4.0.0 - 2017-06-02 3 | * https://github.com/bryanbraun/anchorjs 4 | * Copyright (c) 2017 Bryan Braun; Licensed MIT 5 | */ 6 | /* eslint-env amd, node */ 7 | 8 | // https://github.com/umdjs/umd/blob/master/templates/returnExports.js 9 | (function(root, factory) { 10 | 'use strict'; 11 | if (typeof define === 'function' && define.amd) { 12 | // AMD. Register as an anonymous module. 13 | define([], factory); 14 | } else if (typeof module === 'object' && module.exports) { 15 | // Node. Does not work with strict CommonJS, but 16 | // only CommonJS-like environments that support module.exports, 17 | // like Node. 18 | module.exports = factory(); 19 | } else { 20 | // Browser globals (root is window) 21 | root.AnchorJS = factory(); 22 | root.anchors = new root.AnchorJS(); 23 | } 24 | })(this, function() { 25 | 'use strict'; 26 | function AnchorJS(options) { 27 | this.options = options || {}; 28 | this.elements = []; 29 | 30 | /** 31 | * Assigns options to the internal options object, and provides defaults. 32 | * @param {Object} opts - Options object 33 | */ 34 | function _applyRemainingDefaultOptions(opts) { 35 | opts.icon = opts.hasOwnProperty('icon') ? opts.icon : '\ue9cb'; // Accepts characters (and also URLs?), like '#', '¶', '❡', or '§'. 36 | opts.visible = opts.hasOwnProperty('visible') ? opts.visible : 'hover'; // Also accepts 'always' & 'touch' 37 | opts.placement = opts.hasOwnProperty('placement') 38 | ? opts.placement 39 | : 'right'; // Also accepts 'left' 40 | opts.class = opts.hasOwnProperty('class') ? opts.class : ''; // Accepts any class name. 41 | // Using Math.floor here will ensure the value is Number-cast and an integer. 42 | opts.truncate = opts.hasOwnProperty('truncate') 43 | ? Math.floor(opts.truncate) 44 | : 64; // Accepts any value that can be typecast to a number. 45 | } 46 | 47 | _applyRemainingDefaultOptions(this.options); 48 | 49 | /** 50 | * Checks to see if this device supports touch. Uses criteria pulled from Modernizr: 51 | * https://github.com/Modernizr/Modernizr/blob/da22eb27631fc4957f67607fe6042e85c0a84656/feature-detects/touchevents.js#L40 52 | * @returns {Boolean} - true if the current device supports touch. 53 | */ 54 | this.isTouchDevice = function() { 55 | return !!( 56 | 'ontouchstart' in window || 57 | (window.DocumentTouch && document instanceof DocumentTouch) 58 | ); 59 | }; 60 | 61 | /** 62 | * Add anchor links to page elements. 63 | * @param {String|Array|Nodelist} selector - A CSS selector for targeting the elements you wish to add anchor links 64 | * to. Also accepts an array or nodeList containing the relavant elements. 65 | * @returns {this} - The AnchorJS object 66 | */ 67 | this.add = function(selector) { 68 | var elements, 69 | elsWithIds, 70 | idList, 71 | elementID, 72 | i, 73 | index, 74 | count, 75 | tidyText, 76 | newTidyText, 77 | readableID, 78 | anchor, 79 | visibleOptionToUse, 80 | indexesToDrop = []; 81 | 82 | // We reapply options here because somebody may have overwritten the default options object when setting options. 83 | // For example, this overwrites all options but visible: 84 | // 85 | // anchors.options = { visible: 'always'; } 86 | _applyRemainingDefaultOptions(this.options); 87 | 88 | visibleOptionToUse = this.options.visible; 89 | if (visibleOptionToUse === 'touch') { 90 | visibleOptionToUse = this.isTouchDevice() ? 'always' : 'hover'; 91 | } 92 | 93 | // Provide a sensible default selector, if none is given. 94 | if (!selector) { 95 | selector = 'h2, h3, h4, h5, h6'; 96 | } 97 | 98 | elements = _getElements(selector); 99 | 100 | if (elements.length === 0) { 101 | return this; 102 | } 103 | 104 | _addBaselineStyles(); 105 | 106 | // We produce a list of existing IDs so we don't generate a duplicate. 107 | elsWithIds = document.querySelectorAll('[id]'); 108 | idList = [].map.call(elsWithIds, function assign(el) { 109 | return el.id; 110 | }); 111 | 112 | for (i = 0; i < elements.length; i++) { 113 | if (this.hasAnchorJSLink(elements[i])) { 114 | indexesToDrop.push(i); 115 | continue; 116 | } 117 | 118 | if (elements[i].hasAttribute('id')) { 119 | elementID = elements[i].getAttribute('id'); 120 | } else if (elements[i].hasAttribute('data-anchor-id')) { 121 | elementID = elements[i].getAttribute('data-anchor-id'); 122 | } else { 123 | tidyText = this.urlify(elements[i].textContent); 124 | 125 | // Compare our generated ID to existing IDs (and increment it if needed) 126 | // before we add it to the page. 127 | newTidyText = tidyText; 128 | count = 0; 129 | do { 130 | if (index !== undefined) { 131 | newTidyText = tidyText + '-' + count; 132 | } 133 | 134 | index = idList.indexOf(newTidyText); 135 | count += 1; 136 | } while (index !== -1); 137 | index = undefined; 138 | idList.push(newTidyText); 139 | 140 | elements[i].setAttribute('id', newTidyText); 141 | elementID = newTidyText; 142 | } 143 | 144 | readableID = elementID.replace(/-/g, ' '); 145 | 146 | // The following code builds the following DOM structure in a more effiecient (albeit opaque) way. 147 | // ''; 148 | anchor = document.createElement('a'); 149 | anchor.className = 'anchorjs-link ' + this.options.class; 150 | anchor.href = '#' + elementID; 151 | anchor.setAttribute('aria-label', 'Anchor link for: ' + readableID); 152 | anchor.setAttribute('data-anchorjs-icon', this.options.icon); 153 | 154 | if (visibleOptionToUse === 'always') { 155 | anchor.style.opacity = '1'; 156 | } 157 | 158 | if (this.options.icon === '\ue9cb') { 159 | anchor.style.font = '1em/1 anchorjs-icons'; 160 | 161 | // We set lineHeight = 1 here because the `anchorjs-icons` font family could otherwise affect the 162 | // height of the heading. This isn't the case for icons with `placement: left`, so we restore 163 | // line-height: inherit in that case, ensuring they remain positioned correctly. For more info, 164 | // see https://github.com/bryanbraun/anchorjs/issues/39. 165 | if (this.options.placement === 'left') { 166 | anchor.style.lineHeight = 'inherit'; 167 | } 168 | } 169 | 170 | if (this.options.placement === 'left') { 171 | anchor.style.position = 'absolute'; 172 | anchor.style.marginLeft = '-1em'; 173 | anchor.style.paddingRight = '0.5em'; 174 | elements[i].insertBefore(anchor, elements[i].firstChild); 175 | } else { 176 | // if the option provided is `right` (or anything else). 177 | anchor.style.paddingLeft = '0.375em'; 178 | elements[i].appendChild(anchor); 179 | } 180 | } 181 | 182 | for (i = 0; i < indexesToDrop.length; i++) { 183 | elements.splice(indexesToDrop[i] - i, 1); 184 | } 185 | this.elements = this.elements.concat(elements); 186 | 187 | return this; 188 | }; 189 | 190 | /** 191 | * Removes all anchorjs-links from elements targed by the selector. 192 | * @param {String|Array|Nodelist} selector - A CSS selector string targeting elements with anchor links, 193 | * OR a nodeList / array containing the DOM elements. 194 | * @returns {this} - The AnchorJS object 195 | */ 196 | this.remove = function(selector) { 197 | var index, 198 | domAnchor, 199 | elements = _getElements(selector); 200 | 201 | for (var i = 0; i < elements.length; i++) { 202 | domAnchor = elements[i].querySelector('.anchorjs-link'); 203 | if (domAnchor) { 204 | // Drop the element from our main list, if it's in there. 205 | index = this.elements.indexOf(elements[i]); 206 | if (index !== -1) { 207 | this.elements.splice(index, 1); 208 | } 209 | // Remove the anchor from the DOM. 210 | elements[i].removeChild(domAnchor); 211 | } 212 | } 213 | return this; 214 | }; 215 | 216 | /** 217 | * Removes all anchorjs links. Mostly used for tests. 218 | */ 219 | this.removeAll = function() { 220 | this.remove(this.elements); 221 | }; 222 | 223 | /** 224 | * Urlify - Refine text so it makes a good ID. 225 | * 226 | * To do this, we remove apostrophes, replace nonsafe characters with hyphens, 227 | * remove extra hyphens, truncate, trim hyphens, and make lowercase. 228 | * 229 | * @param {String} text - Any text. Usually pulled from the webpage element we are linking to. 230 | * @returns {String} - hyphen-delimited text for use in IDs and URLs. 231 | */ 232 | this.urlify = function(text) { 233 | // Regex for finding the nonsafe URL characters (many need escaping): & +$,:;=?@"#{}|^~[`%!'<>]./()*\ 234 | var nonsafeChars = /[& +$,:;=?@"#{}|^~[`%!'<>\]\.\/\(\)\*\\]/g, 235 | urlText; 236 | 237 | // The reason we include this _applyRemainingDefaultOptions is so urlify can be called independently, 238 | // even after setting options. This can be useful for tests or other applications. 239 | if (!this.options.truncate) { 240 | _applyRemainingDefaultOptions(this.options); 241 | } 242 | 243 | // Note: we trim hyphens after truncating because truncating can cause dangling hyphens. 244 | // Example string: // " ⚡⚡ Don't forget: URL fragments should be i18n-friendly, hyphenated, short, and clean." 245 | urlText = text 246 | .trim() // "⚡⚡ Don't forget: URL fragments should be i18n-friendly, hyphenated, short, and clean." 247 | .replace(/\'/gi, '') // "⚡⚡ Dont forget: URL fragments should be i18n-friendly, hyphenated, short, and clean." 248 | .replace(nonsafeChars, '-') // "⚡⚡-Dont-forget--URL-fragments-should-be-i18n-friendly--hyphenated--short--and-clean-" 249 | .replace(/-{2,}/g, '-') // "⚡⚡-Dont-forget-URL-fragments-should-be-i18n-friendly-hyphenated-short-and-clean-" 250 | .substring(0, this.options.truncate) // "⚡⚡-Dont-forget-URL-fragments-should-be-i18n-friendly-hyphenated-" 251 | .replace(/^-+|-+$/gm, '') // "⚡⚡-Dont-forget-URL-fragments-should-be-i18n-friendly-hyphenated" 252 | .toLowerCase(); // "⚡⚡-dont-forget-url-fragments-should-be-i18n-friendly-hyphenated" 253 | 254 | return urlText; 255 | }; 256 | 257 | /** 258 | * Determines if this element already has an AnchorJS link on it. 259 | * Uses this technique: http://stackoverflow.com/a/5898748/1154642 260 | * @param {HTMLElemnt} el - a DOM node 261 | * @returns {Boolean} true/false 262 | */ 263 | this.hasAnchorJSLink = function(el) { 264 | var hasLeftAnchor = 265 | el.firstChild && 266 | (' ' + el.firstChild.className + ' ').indexOf(' anchorjs-link ') > -1, 267 | hasRightAnchor = 268 | el.lastChild && 269 | (' ' + el.lastChild.className + ' ').indexOf(' anchorjs-link ') > -1; 270 | 271 | return hasLeftAnchor || hasRightAnchor || false; 272 | }; 273 | 274 | /** 275 | * Turns a selector, nodeList, or array of elements into an array of elements (so we can use array methods). 276 | * It also throws errors on any other inputs. Used to handle inputs to .add and .remove. 277 | * @param {String|Array|Nodelist} input - A CSS selector string targeting elements with anchor links, 278 | * OR a nodeList / array containing the DOM elements. 279 | * @returns {Array} - An array containing the elements we want. 280 | */ 281 | function _getElements(input) { 282 | var elements; 283 | if (typeof input === 'string' || input instanceof String) { 284 | // See https://davidwalsh.name/nodelist-array for the technique transforming nodeList -> Array. 285 | elements = [].slice.call(document.querySelectorAll(input)); 286 | // I checked the 'input instanceof NodeList' test in IE9 and modern browsers and it worked for me. 287 | } else if (Array.isArray(input) || input instanceof NodeList) { 288 | elements = [].slice.call(input); 289 | } else { 290 | throw new Error('The selector provided to AnchorJS was invalid.'); 291 | } 292 | return elements; 293 | } 294 | 295 | /** 296 | * _addBaselineStyles 297 | * Adds baseline styles to the page, used by all AnchorJS links irregardless of configuration. 298 | */ 299 | function _addBaselineStyles() { 300 | // We don't want to add global baseline styles if they've been added before. 301 | if (document.head.querySelector('style.anchorjs') !== null) { 302 | return; 303 | } 304 | 305 | var style = document.createElement('style'), 306 | linkRule = 307 | ' .anchorjs-link {' + 308 | ' opacity: 0;' + 309 | ' text-decoration: none;' + 310 | ' -webkit-font-smoothing: antialiased;' + 311 | ' -moz-osx-font-smoothing: grayscale;' + 312 | ' }', 313 | hoverRule = 314 | ' *:hover > .anchorjs-link,' + 315 | ' .anchorjs-link:focus {' + 316 | ' opacity: 1;' + 317 | ' }', 318 | anchorjsLinkFontFace = 319 | ' @font-face {' + 320 | ' font-family: "anchorjs-icons";' + // Icon from icomoon; 10px wide & 10px tall; 2 empty below & 4 above 321 | ' src: url(data:n/a;base64,AAEAAAALAIAAAwAwT1MvMg8yG2cAAAE4AAAAYGNtYXDp3gC3AAABpAAAAExnYXNwAAAAEAAAA9wAAAAIZ2x5ZlQCcfwAAAH4AAABCGhlYWQHFvHyAAAAvAAAADZoaGVhBnACFwAAAPQAAAAkaG10eASAADEAAAGYAAAADGxvY2EACACEAAAB8AAAAAhtYXhwAAYAVwAAARgAAAAgbmFtZQGOH9cAAAMAAAAAunBvc3QAAwAAAAADvAAAACAAAQAAAAEAAHzE2p9fDzz1AAkEAAAAAADRecUWAAAAANQA6R8AAAAAAoACwAAAAAgAAgAAAAAAAAABAAADwP/AAAACgAAA/9MCrQABAAAAAAAAAAAAAAAAAAAAAwABAAAAAwBVAAIAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAMCQAGQAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAg//0DwP/AAEADwABAAAAAAQAAAAAAAAAAAAAAIAAAAAAAAAIAAAACgAAxAAAAAwAAAAMAAAAcAAEAAwAAABwAAwABAAAAHAAEADAAAAAIAAgAAgAAACDpy//9//8AAAAg6cv//f///+EWNwADAAEAAAAAAAAAAAAAAAAACACEAAEAAAAAAAAAAAAAAAAxAAACAAQARAKAAsAAKwBUAAABIiYnJjQ3NzY2MzIWFxYUBwcGIicmNDc3NjQnJiYjIgYHBwYUFxYUBwYGIwciJicmNDc3NjIXFhQHBwYUFxYWMzI2Nzc2NCcmNDc2MhcWFAcHBgYjARQGDAUtLXoWOR8fORYtLTgKGwoKCjgaGg0gEhIgDXoaGgkJBQwHdR85Fi0tOAobCgoKOBoaDSASEiANehoaCQkKGwotLXoWOR8BMwUFLYEuehYXFxYugC44CQkKGwo4GkoaDQ0NDXoaShoKGwoFBe8XFi6ALjgJCQobCjgaShoNDQ0NehpKGgobCgoKLYEuehYXAAAADACWAAEAAAAAAAEACAAAAAEAAAAAAAIAAwAIAAEAAAAAAAMACAAAAAEAAAAAAAQACAAAAAEAAAAAAAUAAQALAAEAAAAAAAYACAAAAAMAAQQJAAEAEAAMAAMAAQQJAAIABgAcAAMAAQQJAAMAEAAMAAMAAQQJAAQAEAAMAAMAAQQJAAUAAgAiAAMAAQQJAAYAEAAMYW5jaG9yanM0MDBAAGEAbgBjAGgAbwByAGoAcwA0ADAAMABAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAH//wAP) format("truetype");' + 322 | ' }', 323 | pseudoElContent = 324 | ' [data-anchorjs-icon]::after {' + 325 | ' content: attr(data-anchorjs-icon);' + 326 | ' }', 327 | firstStyleEl; 328 | 329 | style.className = 'anchorjs'; 330 | style.appendChild(document.createTextNode('')); // Necessary for Webkit. 331 | 332 | // We place it in the head with the other style tags, if possible, so as to 333 | // not look out of place. We insert before the others so these styles can be 334 | // overridden if necessary. 335 | firstStyleEl = document.head.querySelector('[rel="stylesheet"], style'); 336 | if (firstStyleEl === undefined) { 337 | document.head.appendChild(style); 338 | } else { 339 | document.head.insertBefore(style, firstStyleEl); 340 | } 341 | 342 | style.sheet.insertRule(linkRule, style.sheet.cssRules.length); 343 | style.sheet.insertRule(hoverRule, style.sheet.cssRules.length); 344 | style.sheet.insertRule(pseudoElContent, style.sheet.cssRules.length); 345 | style.sheet.insertRule(anchorjsLinkFontFace, style.sheet.cssRules.length); 346 | } 347 | } 348 | 349 | return AnchorJS; 350 | }); 351 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ml-fnn 5.0.0 | Documentation 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 |

ml-fnn

18 |
5.0.0
19 | 24 |
25 | 79 |
80 | 83 |
84 |
85 |
86 | 87 | 88 |
89 | 90 | 91 |
92 | 93 |

94 | FeedForwardNeuralNetworks 95 |

96 | 97 | 98 | 99 | FeedForwardNeuralNetwork.js 100 | 101 | 102 |
103 | 104 | 105 |

Create a new Feedforward neural network model.

106 | 107 |
new FeedForwardNeuralNetworks(options: object?)
108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 |
Parameters
120 |
121 | 122 |
123 |
124 | options (object?) 125 | 126 |
127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 146 | 148 | 149 | 150 | 151 | 152 | 153 | 157 | 159 | 160 | 161 | 162 | 163 | 164 | 168 | 170 | 171 | 172 | 173 | 174 | 175 | 179 | 181 | 182 | 183 | 184 | 185 | 186 | 190 | 194 | 195 | 196 | 197 | 198 | 199 | 203 | 205 | 206 | 207 | 208 | 209 | 210 |
NameDescription
options.hiddenLayers Array 143 | 144 | (default [10]) 145 | Array that contains the sizes of the hidden layers. 147 |
options.iterations number 154 | 155 | (default 50) 156 | Number of iterations at the training step. 158 |
options.learningRate number 165 | 166 | (default 0.01) 167 | Learning rate of the neural net (also known as epsilon). 169 |
options.regularization number 176 | 177 | (default 0.01) 178 | Regularization parameter af the neural net. 180 |
options.activation string 187 | 188 | (default 'tanh') 189 | activation function to be used. (options: 'tanh'(default), 191 | 'identity', 'logistic', 'arctan', 'softsign', 'relu', 'softplus', 'bent', 'sinusoid', 'sinc', 'gaussian'). 192 | (single-parametric options: 'parametric-relu', 'exponential-relu', 'soft-exponential'). 193 |
options.activationParam number 200 | 201 | (default 1) 202 | if the selected activation function needs a parameter. 204 |
211 | 212 |
213 | 214 |
215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 |
Static Members
227 |
228 | 229 |
230 |
231 |
232 | 233 | load(model) 234 |
235 |
236 | 304 |
305 | 306 |
307 | 308 | 309 | 310 | 311 |
Instance Members
312 |
313 | 314 |
315 |
316 |
317 | 318 | predict(features) 319 |
320 |
321 | 389 |
390 | 391 |
392 |
393 |
394 | 395 | toJSON() 396 |
397 |
398 | 453 |
454 | 455 |
456 |
457 |
458 | 459 | train(features, labels) 460 |
461 |
462 | 530 |
531 | 532 |
533 | 534 | 535 | 536 | 537 | 538 | 539 |
540 | 541 | 542 | 543 |
544 |
545 | 546 | 547 | 548 | 549 | 550 | -------------------------------------------------------------------------------- /docs/assets/split.js: -------------------------------------------------------------------------------- 1 | /*! Split.js - v1.5.11 */ 2 | 3 | (function (global, factory) { 4 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 5 | typeof define === 'function' && define.amd ? define(factory) : 6 | (global.Split = factory()); 7 | }(this, (function () { 'use strict'; 8 | 9 | // The programming goals of Split.js are to deliver readable, understandable and 10 | // maintainable code, while at the same time manually optimizing for tiny minified file size, 11 | // browser compatibility without additional requirements, graceful fallback (IE8 is supported) 12 | // and very few assumptions about the user's page layout. 13 | var global = window; 14 | var document = global.document; 15 | 16 | // Save a couple long function names that are used frequently. 17 | // This optimization saves around 400 bytes. 18 | var addEventListener = 'addEventListener'; 19 | var removeEventListener = 'removeEventListener'; 20 | var getBoundingClientRect = 'getBoundingClientRect'; 21 | var gutterStartDragging = '_a'; 22 | var aGutterSize = '_b'; 23 | var bGutterSize = '_c'; 24 | var HORIZONTAL = 'horizontal'; 25 | var NOOP = function () { return false; }; 26 | 27 | // Figure out if we're in IE8 or not. IE8 will still render correctly, 28 | // but will be static instead of draggable. 29 | var isIE8 = global.attachEvent && !global[addEventListener]; 30 | 31 | // Helper function determines which prefixes of CSS calc we need. 32 | // We only need to do this once on startup, when this anonymous function is called. 33 | // 34 | // Tests -webkit, -moz and -o prefixes. Modified from StackOverflow: 35 | // http://stackoverflow.com/questions/16625140/js-feature-detection-to-detect-the-usage-of-webkit-calc-over-calc/16625167#16625167 36 | var calc = (['', '-webkit-', '-moz-', '-o-'] 37 | .filter(function (prefix) { 38 | var el = document.createElement('div'); 39 | el.style.cssText = "width:" + prefix + "calc(9px)"; 40 | 41 | return !!el.style.length 42 | }) 43 | .shift()) + "calc"; 44 | 45 | // Helper function checks if its argument is a string-like type 46 | var isString = function (v) { return typeof v === 'string' || v instanceof String; }; 47 | 48 | // Helper function allows elements and string selectors to be used 49 | // interchangeably. In either case an element is returned. This allows us to 50 | // do `Split([elem1, elem2])` as well as `Split(['#id1', '#id2'])`. 51 | var elementOrSelector = function (el) { 52 | if (isString(el)) { 53 | var ele = document.querySelector(el); 54 | if (!ele) { 55 | throw new Error(("Selector " + el + " did not match a DOM element")) 56 | } 57 | return ele 58 | } 59 | 60 | return el 61 | }; 62 | 63 | // Helper function gets a property from the properties object, with a default fallback 64 | var getOption = function (options, propName, def) { 65 | var value = options[propName]; 66 | if (value !== undefined) { 67 | return value 68 | } 69 | return def 70 | }; 71 | 72 | var getGutterSize = function (gutterSize, isFirst, isLast, gutterAlign) { 73 | if (isFirst) { 74 | if (gutterAlign === 'end') { 75 | return 0 76 | } 77 | if (gutterAlign === 'center') { 78 | return gutterSize / 2 79 | } 80 | } else if (isLast) { 81 | if (gutterAlign === 'start') { 82 | return 0 83 | } 84 | if (gutterAlign === 'center') { 85 | return gutterSize / 2 86 | } 87 | } 88 | 89 | return gutterSize 90 | }; 91 | 92 | // Default options 93 | var defaultGutterFn = function (i, gutterDirection) { 94 | var gut = document.createElement('div'); 95 | gut.className = "gutter gutter-" + gutterDirection; 96 | return gut 97 | }; 98 | 99 | var defaultElementStyleFn = function (dim, size, gutSize) { 100 | var style = {}; 101 | 102 | if (!isString(size)) { 103 | if (!isIE8) { 104 | style[dim] = calc + "(" + size + "% - " + gutSize + "px)"; 105 | } else { 106 | style[dim] = size + "%"; 107 | } 108 | } else { 109 | style[dim] = size; 110 | } 111 | 112 | return style 113 | }; 114 | 115 | var defaultGutterStyleFn = function (dim, gutSize) { 116 | var obj; 117 | 118 | return (( obj = {}, obj[dim] = (gutSize + "px"), obj )); 119 | }; 120 | 121 | // The main function to initialize a split. Split.js thinks about each pair 122 | // of elements as an independant pair. Dragging the gutter between two elements 123 | // only changes the dimensions of elements in that pair. This is key to understanding 124 | // how the following functions operate, since each function is bound to a pair. 125 | // 126 | // A pair object is shaped like this: 127 | // 128 | // { 129 | // a: DOM element, 130 | // b: DOM element, 131 | // aMin: Number, 132 | // bMin: Number, 133 | // dragging: Boolean, 134 | // parent: DOM element, 135 | // direction: 'horizontal' | 'vertical' 136 | // } 137 | // 138 | // The basic sequence: 139 | // 140 | // 1. Set defaults to something sane. `options` doesn't have to be passed at all. 141 | // 2. Initialize a bunch of strings based on the direction we're splitting. 142 | // A lot of the behavior in the rest of the library is paramatized down to 143 | // rely on CSS strings and classes. 144 | // 3. Define the dragging helper functions, and a few helpers to go with them. 145 | // 4. Loop through the elements while pairing them off. Every pair gets an 146 | // `pair` object and a gutter. 147 | // 5. Actually size the pair elements, insert gutters and attach event listeners. 148 | var Split = function (idsOption, options) { 149 | if ( options === void 0 ) options = {}; 150 | 151 | var ids = idsOption; 152 | var dimension; 153 | var clientAxis; 154 | var position; 155 | var positionEnd; 156 | var clientSize; 157 | var elements; 158 | 159 | // Allow HTMLCollection to be used as an argument when supported 160 | if (Array.from) { 161 | ids = Array.from(ids); 162 | } 163 | 164 | // All DOM elements in the split should have a common parent. We can grab 165 | // the first elements parent and hope users read the docs because the 166 | // behavior will be whacky otherwise. 167 | var firstElement = elementOrSelector(ids[0]); 168 | var parent = firstElement.parentNode; 169 | var parentStyle = getComputedStyle ? getComputedStyle(parent) : null; 170 | var parentFlexDirection = parentStyle ? parentStyle.flexDirection : null; 171 | 172 | // Set default options.sizes to equal percentages of the parent element. 173 | var sizes = getOption(options, 'sizes') || ids.map(function () { return 100 / ids.length; }); 174 | 175 | // Standardize minSize to an array if it isn't already. This allows minSize 176 | // to be passed as a number. 177 | var minSize = getOption(options, 'minSize', 100); 178 | var minSizes = Array.isArray(minSize) ? minSize : ids.map(function () { return minSize; }); 179 | 180 | // Get other options 181 | var expandToMin = getOption(options, 'expandToMin', false); 182 | var gutterSize = getOption(options, 'gutterSize', 10); 183 | var gutterAlign = getOption(options, 'gutterAlign', 'center'); 184 | var snapOffset = getOption(options, 'snapOffset', 30); 185 | var dragInterval = getOption(options, 'dragInterval', 1); 186 | var direction = getOption(options, 'direction', HORIZONTAL); 187 | var cursor = getOption( 188 | options, 189 | 'cursor', 190 | direction === HORIZONTAL ? 'col-resize' : 'row-resize' 191 | ); 192 | var gutter = getOption(options, 'gutter', defaultGutterFn); 193 | var elementStyle = getOption( 194 | options, 195 | 'elementStyle', 196 | defaultElementStyleFn 197 | ); 198 | var gutterStyle = getOption(options, 'gutterStyle', defaultGutterStyleFn); 199 | 200 | // 2. Initialize a bunch of strings based on the direction we're splitting. 201 | // A lot of the behavior in the rest of the library is paramatized down to 202 | // rely on CSS strings and classes. 203 | if (direction === HORIZONTAL) { 204 | dimension = 'width'; 205 | clientAxis = 'clientX'; 206 | position = 'left'; 207 | positionEnd = 'right'; 208 | clientSize = 'clientWidth'; 209 | } else if (direction === 'vertical') { 210 | dimension = 'height'; 211 | clientAxis = 'clientY'; 212 | position = 'top'; 213 | positionEnd = 'bottom'; 214 | clientSize = 'clientHeight'; 215 | } 216 | 217 | // 3. Define the dragging helper functions, and a few helpers to go with them. 218 | // Each helper is bound to a pair object that contains its metadata. This 219 | // also makes it easy to store references to listeners that that will be 220 | // added and removed. 221 | // 222 | // Even though there are no other functions contained in them, aliasing 223 | // this to self saves 50 bytes or so since it's used so frequently. 224 | // 225 | // The pair object saves metadata like dragging state, position and 226 | // event listener references. 227 | 228 | function setElementSize(el, size, gutSize, i) { 229 | // Split.js allows setting sizes via numbers (ideally), or if you must, 230 | // by string, like '300px'. This is less than ideal, because it breaks 231 | // the fluid layout that `calc(% - px)` provides. You're on your own if you do that, 232 | // make sure you calculate the gutter size by hand. 233 | var style = elementStyle(dimension, size, gutSize, i); 234 | 235 | Object.keys(style).forEach(function (prop) { 236 | // eslint-disable-next-line no-param-reassign 237 | el.style[prop] = style[prop]; 238 | }); 239 | } 240 | 241 | function setGutterSize(gutterElement, gutSize, i) { 242 | var style = gutterStyle(dimension, gutSize, i); 243 | 244 | Object.keys(style).forEach(function (prop) { 245 | // eslint-disable-next-line no-param-reassign 246 | gutterElement.style[prop] = style[prop]; 247 | }); 248 | } 249 | 250 | function getSizes() { 251 | return elements.map(function (element) { return element.size; }) 252 | } 253 | 254 | // Supports touch events, but not multitouch, so only the first 255 | // finger `touches[0]` is counted. 256 | function getMousePosition(e) { 257 | if ('touches' in e) { return e.touches[0][clientAxis] } 258 | return e[clientAxis] 259 | } 260 | 261 | // Actually adjust the size of elements `a` and `b` to `offset` while dragging. 262 | // calc is used to allow calc(percentage + gutterpx) on the whole split instance, 263 | // which allows the viewport to be resized without additional logic. 264 | // Element a's size is the same as offset. b's size is total size - a size. 265 | // Both sizes are calculated from the initial parent percentage, 266 | // then the gutter size is subtracted. 267 | function adjust(offset) { 268 | var a = elements[this.a]; 269 | var b = elements[this.b]; 270 | var percentage = a.size + b.size; 271 | 272 | a.size = (offset / this.size) * percentage; 273 | b.size = percentage - (offset / this.size) * percentage; 274 | 275 | setElementSize(a.element, a.size, this[aGutterSize], a.i); 276 | setElementSize(b.element, b.size, this[bGutterSize], b.i); 277 | } 278 | 279 | // drag, where all the magic happens. The logic is really quite simple: 280 | // 281 | // 1. Ignore if the pair is not dragging. 282 | // 2. Get the offset of the event. 283 | // 3. Snap offset to min if within snappable range (within min + snapOffset). 284 | // 4. Actually adjust each element in the pair to offset. 285 | // 286 | // --------------------------------------------------------------------- 287 | // | | <- a.minSize || b.minSize -> | | 288 | // | | | <- this.snapOffset || this.snapOffset -> | | | 289 | // | | | || | | | 290 | // | | | || | | | 291 | // --------------------------------------------------------------------- 292 | // | <- this.start this.size -> | 293 | function drag(e) { 294 | var offset; 295 | var a = elements[this.a]; 296 | var b = elements[this.b]; 297 | 298 | if (!this.dragging) { return } 299 | 300 | // Get the offset of the event from the first side of the 301 | // pair `this.start`. Then offset by the initial position of the 302 | // mouse compared to the gutter size. 303 | offset = 304 | getMousePosition(e) - 305 | this.start + 306 | (this[aGutterSize] - this.dragOffset); 307 | 308 | if (dragInterval > 1) { 309 | offset = Math.round(offset / dragInterval) * dragInterval; 310 | } 311 | 312 | // If within snapOffset of min or max, set offset to min or max. 313 | // snapOffset buffers a.minSize and b.minSize, so logic is opposite for both. 314 | // Include the appropriate gutter sizes to prevent overflows. 315 | if (offset <= a.minSize + snapOffset + this[aGutterSize]) { 316 | offset = a.minSize + this[aGutterSize]; 317 | } else if ( 318 | offset >= 319 | this.size - (b.minSize + snapOffset + this[bGutterSize]) 320 | ) { 321 | offset = this.size - (b.minSize + this[bGutterSize]); 322 | } 323 | 324 | // Actually adjust the size. 325 | adjust.call(this, offset); 326 | 327 | // Call the drag callback continously. Don't do anything too intensive 328 | // in this callback. 329 | getOption(options, 'onDrag', NOOP)(); 330 | } 331 | 332 | // Cache some important sizes when drag starts, so we don't have to do that 333 | // continously: 334 | // 335 | // `size`: The total size of the pair. First + second + first gutter + second gutter. 336 | // `start`: The leading side of the first element. 337 | // 338 | // ------------------------------------------------ 339 | // | aGutterSize -> ||| | 340 | // | ||| | 341 | // | ||| | 342 | // | ||| <- bGutterSize | 343 | // ------------------------------------------------ 344 | // | <- start size -> | 345 | function calculateSizes() { 346 | // Figure out the parent size minus padding. 347 | var a = elements[this.a].element; 348 | var b = elements[this.b].element; 349 | 350 | var aBounds = a[getBoundingClientRect](); 351 | var bBounds = b[getBoundingClientRect](); 352 | 353 | this.size = 354 | aBounds[dimension] + 355 | bBounds[dimension] + 356 | this[aGutterSize] + 357 | this[bGutterSize]; 358 | this.start = aBounds[position]; 359 | this.end = aBounds[positionEnd]; 360 | } 361 | 362 | function innerSize(element) { 363 | // Return nothing if getComputedStyle is not supported (< IE9) 364 | // Or if parent element has no layout yet 365 | if (!getComputedStyle) { return null } 366 | 367 | var computedStyle = getComputedStyle(element); 368 | 369 | if (!computedStyle) { return null } 370 | 371 | var size = element[clientSize]; 372 | 373 | if (size === 0) { return null } 374 | 375 | if (direction === HORIZONTAL) { 376 | size -= 377 | parseFloat(computedStyle.paddingLeft) + 378 | parseFloat(computedStyle.paddingRight); 379 | } else { 380 | size -= 381 | parseFloat(computedStyle.paddingTop) + 382 | parseFloat(computedStyle.paddingBottom); 383 | } 384 | 385 | return size 386 | } 387 | 388 | // When specifying percentage sizes that are less than the computed 389 | // size of the element minus the gutter, the lesser percentages must be increased 390 | // (and decreased from the other elements) to make space for the pixels 391 | // subtracted by the gutters. 392 | function trimToMin(sizesToTrim) { 393 | // Try to get inner size of parent element. 394 | // If it's no supported, return original sizes. 395 | var parentSize = innerSize(parent); 396 | if (parentSize === null) { 397 | return sizesToTrim 398 | } 399 | 400 | if (minSizes.reduce(function (a, b) { return a + b; }, 0) > parentSize) { 401 | return sizesToTrim 402 | } 403 | 404 | // Keep track of the excess pixels, the amount of pixels over the desired percentage 405 | // Also keep track of the elements with pixels to spare, to decrease after if needed 406 | var excessPixels = 0; 407 | var toSpare = []; 408 | 409 | var pixelSizes = sizesToTrim.map(function (size, i) { 410 | // Convert requested percentages to pixel sizes 411 | var pixelSize = (parentSize * size) / 100; 412 | var elementGutterSize = getGutterSize( 413 | gutterSize, 414 | i === 0, 415 | i === sizesToTrim.length - 1, 416 | gutterAlign 417 | ); 418 | var elementMinSize = minSizes[i] + elementGutterSize; 419 | 420 | // If element is too smal, increase excess pixels by the difference 421 | // and mark that it has no pixels to spare 422 | if (pixelSize < elementMinSize) { 423 | excessPixels += elementMinSize - pixelSize; 424 | toSpare.push(0); 425 | return elementMinSize 426 | } 427 | 428 | // Otherwise, mark the pixels it has to spare and return it's original size 429 | toSpare.push(pixelSize - elementMinSize); 430 | return pixelSize 431 | }); 432 | 433 | // If nothing was adjusted, return the original sizes 434 | if (excessPixels === 0) { 435 | return sizesToTrim 436 | } 437 | 438 | return pixelSizes.map(function (pixelSize, i) { 439 | var newPixelSize = pixelSize; 440 | 441 | // While there's still pixels to take, and there's enough pixels to spare, 442 | // take as many as possible up to the total excess pixels 443 | if (excessPixels > 0 && toSpare[i] - excessPixels > 0) { 444 | var takenPixels = Math.min( 445 | excessPixels, 446 | toSpare[i] - excessPixels 447 | ); 448 | 449 | // Subtract the amount taken for the next iteration 450 | excessPixels -= takenPixels; 451 | newPixelSize = pixelSize - takenPixels; 452 | } 453 | 454 | // Return the pixel size adjusted as a percentage 455 | return (newPixelSize / parentSize) * 100 456 | }) 457 | } 458 | 459 | // stopDragging is very similar to startDragging in reverse. 460 | function stopDragging() { 461 | var self = this; 462 | var a = elements[self.a].element; 463 | var b = elements[self.b].element; 464 | 465 | if (self.dragging) { 466 | getOption(options, 'onDragEnd', NOOP)(getSizes()); 467 | } 468 | 469 | self.dragging = false; 470 | 471 | // Remove the stored event listeners. This is why we store them. 472 | global[removeEventListener]('mouseup', self.stop); 473 | global[removeEventListener]('touchend', self.stop); 474 | global[removeEventListener]('touchcancel', self.stop); 475 | global[removeEventListener]('mousemove', self.move); 476 | global[removeEventListener]('touchmove', self.move); 477 | 478 | // Clear bound function references 479 | self.stop = null; 480 | self.move = null; 481 | 482 | a[removeEventListener]('selectstart', NOOP); 483 | a[removeEventListener]('dragstart', NOOP); 484 | b[removeEventListener]('selectstart', NOOP); 485 | b[removeEventListener]('dragstart', NOOP); 486 | 487 | a.style.userSelect = ''; 488 | a.style.webkitUserSelect = ''; 489 | a.style.MozUserSelect = ''; 490 | a.style.pointerEvents = ''; 491 | 492 | b.style.userSelect = ''; 493 | b.style.webkitUserSelect = ''; 494 | b.style.MozUserSelect = ''; 495 | b.style.pointerEvents = ''; 496 | 497 | self.gutter.style.cursor = ''; 498 | self.parent.style.cursor = ''; 499 | document.body.style.cursor = ''; 500 | } 501 | 502 | // startDragging calls `calculateSizes` to store the inital size in the pair object. 503 | // It also adds event listeners for mouse/touch events, 504 | // and prevents selection while dragging so avoid the selecting text. 505 | function startDragging(e) { 506 | // Right-clicking can't start dragging. 507 | if ('button' in e && e.button !== 0) { 508 | return 509 | } 510 | 511 | // Alias frequently used variables to save space. 200 bytes. 512 | var self = this; 513 | var a = elements[self.a].element; 514 | var b = elements[self.b].element; 515 | 516 | // Call the onDragStart callback. 517 | if (!self.dragging) { 518 | getOption(options, 'onDragStart', NOOP)(getSizes()); 519 | } 520 | 521 | // Don't actually drag the element. We emulate that in the drag function. 522 | e.preventDefault(); 523 | 524 | // Set the dragging property of the pair object. 525 | self.dragging = true; 526 | 527 | // Create two event listeners bound to the same pair object and store 528 | // them in the pair object. 529 | self.move = drag.bind(self); 530 | self.stop = stopDragging.bind(self); 531 | 532 | // All the binding. `window` gets the stop events in case we drag out of the elements. 533 | global[addEventListener]('mouseup', self.stop); 534 | global[addEventListener]('touchend', self.stop); 535 | global[addEventListener]('touchcancel', self.stop); 536 | global[addEventListener]('mousemove', self.move); 537 | global[addEventListener]('touchmove', self.move); 538 | 539 | // Disable selection. Disable! 540 | a[addEventListener]('selectstart', NOOP); 541 | a[addEventListener]('dragstart', NOOP); 542 | b[addEventListener]('selectstart', NOOP); 543 | b[addEventListener]('dragstart', NOOP); 544 | 545 | a.style.userSelect = 'none'; 546 | a.style.webkitUserSelect = 'none'; 547 | a.style.MozUserSelect = 'none'; 548 | a.style.pointerEvents = 'none'; 549 | 550 | b.style.userSelect = 'none'; 551 | b.style.webkitUserSelect = 'none'; 552 | b.style.MozUserSelect = 'none'; 553 | b.style.pointerEvents = 'none'; 554 | 555 | // Set the cursor at multiple levels 556 | self.gutter.style.cursor = cursor; 557 | self.parent.style.cursor = cursor; 558 | document.body.style.cursor = cursor; 559 | 560 | // Cache the initial sizes of the pair. 561 | calculateSizes.call(self); 562 | 563 | // Determine the position of the mouse compared to the gutter 564 | self.dragOffset = getMousePosition(e) - self.end; 565 | } 566 | 567 | // adjust sizes to ensure percentage is within min size and gutter. 568 | sizes = trimToMin(sizes); 569 | 570 | // 5. Create pair and element objects. Each pair has an index reference to 571 | // elements `a` and `b` of the pair (first and second elements). 572 | // Loop through the elements while pairing them off. Every pair gets a 573 | // `pair` object and a gutter. 574 | // 575 | // Basic logic: 576 | // 577 | // - Starting with the second element `i > 0`, create `pair` objects with 578 | // `a = i - 1` and `b = i` 579 | // - Set gutter sizes based on the _pair_ being first/last. The first and last 580 | // pair have gutterSize / 2, since they only have one half gutter, and not two. 581 | // - Create gutter elements and add event listeners. 582 | // - Set the size of the elements, minus the gutter sizes. 583 | // 584 | // ----------------------------------------------------------------------- 585 | // | i=0 | i=1 | i=2 | i=3 | 586 | // | | | | | 587 | // | pair 0 pair 1 pair 2 | 588 | // | | | | | 589 | // ----------------------------------------------------------------------- 590 | var pairs = []; 591 | elements = ids.map(function (id, i) { 592 | // Create the element object. 593 | var element = { 594 | element: elementOrSelector(id), 595 | size: sizes[i], 596 | minSize: minSizes[i], 597 | i: i, 598 | }; 599 | 600 | var pair; 601 | 602 | if (i > 0) { 603 | // Create the pair object with its metadata. 604 | pair = { 605 | a: i - 1, 606 | b: i, 607 | dragging: false, 608 | direction: direction, 609 | parent: parent, 610 | }; 611 | 612 | pair[aGutterSize] = getGutterSize( 613 | gutterSize, 614 | i - 1 === 0, 615 | false, 616 | gutterAlign 617 | ); 618 | pair[bGutterSize] = getGutterSize( 619 | gutterSize, 620 | false, 621 | i === ids.length - 1, 622 | gutterAlign 623 | ); 624 | 625 | // if the parent has a reverse flex-direction, switch the pair elements. 626 | if ( 627 | parentFlexDirection === 'row-reverse' || 628 | parentFlexDirection === 'column-reverse' 629 | ) { 630 | var temp = pair.a; 631 | pair.a = pair.b; 632 | pair.b = temp; 633 | } 634 | } 635 | 636 | // Determine the size of the current element. IE8 is supported by 637 | // staticly assigning sizes without draggable gutters. Assigns a string 638 | // to `size`. 639 | // 640 | // IE9 and above 641 | if (!isIE8) { 642 | // Create gutter elements for each pair. 643 | if (i > 0) { 644 | var gutterElement = gutter(i, direction, element.element); 645 | setGutterSize(gutterElement, gutterSize, i); 646 | 647 | // Save bound event listener for removal later 648 | pair[gutterStartDragging] = startDragging.bind(pair); 649 | 650 | // Attach bound event listener 651 | gutterElement[addEventListener]( 652 | 'mousedown', 653 | pair[gutterStartDragging] 654 | ); 655 | gutterElement[addEventListener]( 656 | 'touchstart', 657 | pair[gutterStartDragging] 658 | ); 659 | 660 | parent.insertBefore(gutterElement, element.element); 661 | 662 | pair.gutter = gutterElement; 663 | } 664 | } 665 | 666 | setElementSize( 667 | element.element, 668 | element.size, 669 | getGutterSize( 670 | gutterSize, 671 | i === 0, 672 | i === ids.length - 1, 673 | gutterAlign 674 | ), 675 | i 676 | ); 677 | 678 | // After the first iteration, and we have a pair object, append it to the 679 | // list of pairs. 680 | if (i > 0) { 681 | pairs.push(pair); 682 | } 683 | 684 | return element 685 | }); 686 | 687 | function adjustToMin(element) { 688 | var isLast = element.i === pairs.length; 689 | var pair = isLast ? pairs[element.i - 1] : pairs[element.i]; 690 | 691 | calculateSizes.call(pair); 692 | 693 | var size = isLast 694 | ? pair.size - element.minSize - pair[bGutterSize] 695 | : element.minSize + pair[aGutterSize]; 696 | 697 | adjust.call(pair, size); 698 | } 699 | 700 | elements.forEach(function (element) { 701 | var computedSize = element.element[getBoundingClientRect]()[dimension]; 702 | 703 | if (computedSize < element.minSize) { 704 | if (expandToMin) { 705 | adjustToMin(element); 706 | } else { 707 | // eslint-disable-next-line no-param-reassign 708 | element.minSize = computedSize; 709 | } 710 | } 711 | }); 712 | 713 | function setSizes(newSizes) { 714 | var trimmed = trimToMin(newSizes); 715 | trimmed.forEach(function (newSize, i) { 716 | if (i > 0) { 717 | var pair = pairs[i - 1]; 718 | 719 | var a = elements[pair.a]; 720 | var b = elements[pair.b]; 721 | 722 | a.size = trimmed[i - 1]; 723 | b.size = newSize; 724 | 725 | setElementSize(a.element, a.size, pair[aGutterSize], a.i); 726 | setElementSize(b.element, b.size, pair[bGutterSize], b.i); 727 | } 728 | }); 729 | } 730 | 731 | function destroy(preserveStyles, preserveGutter) { 732 | pairs.forEach(function (pair) { 733 | if (preserveGutter !== true) { 734 | pair.parent.removeChild(pair.gutter); 735 | } else { 736 | pair.gutter[removeEventListener]( 737 | 'mousedown', 738 | pair[gutterStartDragging] 739 | ); 740 | pair.gutter[removeEventListener]( 741 | 'touchstart', 742 | pair[gutterStartDragging] 743 | ); 744 | } 745 | 746 | if (preserveStyles !== true) { 747 | var style = elementStyle( 748 | dimension, 749 | pair.a.size, 750 | pair[aGutterSize] 751 | ); 752 | 753 | Object.keys(style).forEach(function (prop) { 754 | elements[pair.a].element.style[prop] = ''; 755 | elements[pair.b].element.style[prop] = ''; 756 | }); 757 | } 758 | }); 759 | } 760 | 761 | if (isIE8) { 762 | return { 763 | setSizes: setSizes, 764 | destroy: destroy, 765 | } 766 | } 767 | 768 | return { 769 | setSizes: setSizes, 770 | getSizes: getSizes, 771 | collapse: function collapse(i) { 772 | adjustToMin(elements[i]); 773 | }, 774 | destroy: destroy, 775 | parent: parent, 776 | pairs: pairs, 777 | } 778 | }; 779 | 780 | return Split; 781 | 782 | }))); 783 | --------------------------------------------------------------------------------