Full Code of BrainJS/brain.js for AI

master 7c9db32d9c38 cached
220 files
944.6 KB
255.6k tokens
1061 symbols
1 requests
Download .txt
Showing preview only (1,003K chars total). Download the full file or copy to clipboard to get everything.
Repository: BrainJS/brain.js
Branch: master
Commit: 7c9db32d9c38
Files: 220
Total size: 944.6 KB

Directory structure:
gitextract_h9w2eaqg/

├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .gitattributes
├── .github/
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       └── nodejs.yml
├── .gitignore
├── .npmignore
├── .npmrc
├── .nvmrc
├── .prettierrc
├── .travis.yml
├── .vscode/
│   └── launch.json
├── LICENSE
├── README.md
├── jest.config.json
├── package.json
├── rollup.config.browser.js
├── rollup.config.js
├── src/
│   ├── README.md
│   ├── activation/
│   │   ├── README.md
│   │   ├── index.test.ts
│   │   ├── index.ts
│   │   ├── leaky-relu.test.ts
│   │   ├── leaky-relu.ts
│   │   ├── relu.test.ts
│   │   ├── relu.ts
│   │   ├── sigmoid.test.ts
│   │   ├── sigmoid.ts
│   │   ├── tanh.test.ts
│   │   └── tanh.ts
│   ├── autoencoder.test.ts
│   ├── autoencoder.ts
│   ├── cross-validate.test.ts
│   ├── cross-validate.ts
│   ├── errors/
│   │   └── untrained-neural-network-error.ts
│   ├── estimator/
│   │   └── mean-squared-error.ts
│   ├── feed-forward.end-to-end.test.ts
│   ├── feed-forward.ts
│   ├── feed-forward.unit.test.ts
│   ├── index.ts
│   ├── layer/
│   │   ├── README.md
│   │   ├── activation.test.ts
│   │   ├── activation.ts
│   │   ├── add.test.ts
│   │   ├── add.ts
│   │   ├── arthur-feed-forward.ts
│   │   ├── base-layer.test.ts
│   │   ├── base-layer.ts
│   │   ├── convolution.test.ts
│   │   ├── convolution.ts
│   │   ├── dropout.test.ts
│   │   ├── dropout.ts
│   │   ├── feed-forward.test.ts
│   │   ├── feed-forward.ts
│   │   ├── filter.ts
│   │   ├── fully-connected.test.ts
│   │   ├── fully-connected.ts
│   │   ├── gru.ts
│   │   ├── index.ts
│   │   ├── input.test.ts
│   │   ├── input.ts
│   │   ├── internal.ts
│   │   ├── leaky-relu.test.ts
│   │   ├── leaky-relu.ts
│   │   ├── lstm-cell.test.ts
│   │   ├── lstm-cell.ts
│   │   ├── modifier.ts
│   │   ├── multiply-element.test.ts
│   │   ├── multiply-element.ts
│   │   ├── multiply.test.ts
│   │   ├── multiply.ts
│   │   ├── negative.test.ts
│   │   ├── negative.ts
│   │   ├── ones.ts
│   │   ├── operator.ts
│   │   ├── output.ts
│   │   ├── pool.test.ts
│   │   ├── pool.ts
│   │   ├── random.test.ts
│   │   ├── random.ts
│   │   ├── recurrent-connection.ts
│   │   ├── recurrent-input.ts
│   │   ├── recurrent-zeros.ts
│   │   ├── regression.ts
│   │   ├── relu.test.ts
│   │   ├── relu.ts
│   │   ├── rnn-cell.test.ts
│   │   ├── rnn-cell.ts
│   │   ├── sigmoid.test.ts
│   │   ├── sigmoid.ts
│   │   ├── soft-max.test.ts
│   │   ├── soft-max.ts
│   │   ├── svm.ts
│   │   ├── tanh.test.ts
│   │   ├── tanh.ts
│   │   ├── target.test.ts
│   │   ├── target.ts
│   │   ├── transpose.ts
│   │   ├── types.ts
│   │   └── zeros.ts
│   ├── likely.test.ts
│   ├── likely.ts
│   ├── lookup.test.ts
│   ├── lookup.ts
│   ├── neural-network-gpu.end-to-end.test.ts
│   ├── neural-network-gpu.test.ts
│   ├── neural-network-gpu.ts
│   ├── neural-network-types.ts
│   ├── neural-network.bitwise.test.ts
│   ├── neural-network.json.test.ts
│   ├── neural-network.options.test.ts
│   ├── neural-network.test-method.test.ts
│   ├── neural-network.to-function.test.ts
│   ├── neural-network.trainopts.test.ts
│   ├── neural-network.ts
│   ├── neural-network.unit.test.ts
│   ├── praxis/
│   │   ├── README.md
│   │   ├── arthur-deviation-biases.end-to-end.test.ts
│   │   ├── arthur-deviation-biases.ts
│   │   ├── arthur-deviation-biases.unit.test.ts
│   │   ├── arthur-deviation-weights.end-to-end.test.ts
│   │   ├── arthur-deviation-weights.ts
│   │   ├── arthur-deviation-weights.unit.test.ts
│   │   ├── base-praxis.ts
│   │   ├── index.ts
│   │   ├── momentum-root-mean-squared-propagation.test.ts
│   │   └── momentum-root-mean-squared-propagation.ts
│   ├── recurrent/
│   │   ├── gru-time-step.test.ts
│   │   ├── gru-time-step.ts
│   │   ├── gru.test.ts
│   │   ├── gru.ts
│   │   ├── lstm-time-step.end-to-end.test.ts
│   │   ├── lstm-time-step.test.ts
│   │   ├── lstm-time-step.ts
│   │   ├── lstm.test.ts
│   │   ├── lstm.ts
│   │   ├── matrix/
│   │   │   ├── add-b.ts
│   │   │   ├── add.ts
│   │   │   ├── all-ones.ts
│   │   │   ├── clone-negative.ts
│   │   │   ├── clone.ts
│   │   │   ├── copy.ts
│   │   │   ├── equation.test.ts
│   │   │   ├── equation.ts
│   │   │   ├── index.test.ts
│   │   │   ├── index.ts
│   │   │   ├── max-i.ts
│   │   │   ├── multiply-b.ts
│   │   │   ├── multiply-element-b.ts
│   │   │   ├── multiply-element.ts
│   │   │   ├── multiply.ts
│   │   │   ├── ones-matrix.ts
│   │   │   ├── random-matrix.ts
│   │   │   ├── random-n-matrix.ts
│   │   │   ├── relu-b.ts
│   │   │   ├── relu.ts
│   │   │   ├── row-pluck-b.ts
│   │   │   ├── row-pluck.ts
│   │   │   ├── sample-i.ts
│   │   │   ├── sigmoid-b.ts
│   │   │   ├── sigmoid.ts
│   │   │   ├── softmax.ts
│   │   │   ├── tanh-b.ts
│   │   │   └── tanh.ts
│   │   ├── rnn-data-types.ts
│   │   ├── rnn-time-step.test.ts
│   │   ├── rnn-time-step.ts
│   │   ├── rnn.test.ts
│   │   └── rnn.ts
│   ├── recurrent.baseline.test.ts
│   ├── recurrent.end-to-end.test.ts
│   ├── recurrent.ts
│   ├── recurrent.unit.test.ts
│   ├── test-utils.ts
│   └── utilities/
│       ├── array-lookup-table.ts
│       ├── cast.test.ts
│       ├── cast.ts
│       ├── data-formatter.test.ts
│       ├── data-formatter.ts
│       ├── flatten-layers.test.ts
│       ├── flatten-layers.ts
│       ├── kernel.ts
│       ├── layer-from-json.test.ts
│       ├── layer-from-json.ts
│       ├── layer-setup.ts
│       ├── layer-size.test.ts
│       ├── layer-size.ts
│       ├── lookup-table.ts
│       ├── max.test.ts
│       ├── max.ts
│       ├── mse.test.ts
│       ├── mse.ts
│       ├── ones.test.ts
│       ├── ones.ts
│       ├── random-weight.test.ts
│       ├── random-weight.ts
│       ├── random.test.ts
│       ├── random.ts
│       ├── randos.test.ts
│       ├── randos.ts
│       ├── range.test.ts
│       ├── range.ts
│       ├── to-array.test.ts
│       ├── to-array.ts
│       ├── to-svg.test.ts
│       ├── to-svg.ts
│       ├── traverse-layers-excluding-from.ts
│       ├── traverse-layers-from.ts
│       ├── values-2d.ts
│       ├── values-3d.ts
│       ├── values.ts
│       ├── zeros-2d.ts
│       ├── zeros-3d.ts
│       ├── zeros.test.ts
│       └── zeros.ts
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
root = true

[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false


================================================
FILE: .eslintignore
================================================
# Mac.
.DS_STORE
**.DS_STORE

# Node.
node_modules
npm-debug.log

# Yarn
yarn.lock

# parcel bundler cache
.cache

# code coverage
__coverage__

test*.*

test/unit/coverage/**
test/unit/*.js
test/e2e/*.js
**.min.js
dist/
__coverage__/
index.d.ts


================================================
FILE: .eslintrc
================================================
{
  "env": {
    "es6": true
  },
  "extends": [
    "eslint:recommended",
    "standard-with-typescript",
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:prettier/recommended",
    "prettier/@typescript-eslint"
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "project": "./tsconfig.json",
    "sourceType": "module",
    "ecmaVersion": 2020
  },
  "plugins": ["prettier", "@typescript-eslint"],
  "root": true,
  "rules": {
    "@typescript-eslint/explicit-function-return-type": "off",
    "@typescript-eslint/interface-name-prefix": "off",
    "@typescript-eslint/member-delimiter-style": "off",
    "@typescript-eslint/no-empty-function": "off",
    "@typescript-eslint/no-var-requires": "off",
    "@typescript-eslint/semi": "off",
    "@typescript-eslint/space-before-function-paren": "off",
    "@typescript-eslint/strict-boolean-expressions": "off",
    "arrow-parens": "off",
    "class-methods-use-this": "off",
    "max-classes-per-file": "off",
    "no-continue": "off",
    "no-empty-function": "off",
    "no-multi-assign": "off",
    "no-param-reassign": "off",
    "no-plusplus": "off",
    "no-prototype-builtins": "off",
    "no-restricted-globals": "off",
    "no-underscore-dangle": "off",
    "prettier/prettier": "error",
    "semi": "off",
    "standard/no-callback-literal": "off",
    "no-implied-eval": "off"
  }
}


================================================
FILE: .gitattributes
================================================
dist/* linguist-vendored
examples/* linguist-documentation
*.js linguist-detectable=false


================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contribution Steps:

Thanks for taking the time to contribute to brain.js. Follow these guidelines to make the process smoother:

1. Clone `master` branch

2.  One feature per pull request. Each PR should have one focus, and all the code changes should be supporting that one feature or bug fix. Using a [separate branch](https://guides.github.com/introduction/flow/index.html) for each feature should help you manage developing multiple features at once.

3.  Add/update a test for the feature or fix, if possible. To run these tests:

```bash
npm run test # run tests
```

# Notes: 
- This repository uses `.editorconfig`, `eslint` (`airbnb`) and `prettier` for linting and formating to make coding style consistent throughout the repository, which will automatically run on git `commit`. 

- Please do not run build/dist script and do not bump version number for the script. These things will be handled by the maintainers when necessary.


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug Report 🐞
about: Create a report to help us improve.
labels: bug
---

<!-- If you don't mind add a fun gif or meme, but no pressure -->

![A GIF or MEME to give some spice of the internet](url)

## _What_ is wrong?

<!-- Ex. training network takes really long -->

## _Where_ does it happen?

<!-- Ex. In the a NeuralNetwork when trying to run a net in node.js on my mac -->

## _How_ do we replicate the issue?

<!-- Please be specific as possible. Use dashes (-) or numbers (1.) to create a list of steps -->

- Step 1
- Step 2
- Step 3

## Expected behavior (i.e. solution)

<!-- What do you think should have happened? -->

## Version information

### Nodejs:

### Browser:

### Brain.js:

## _How_ important is this (1-5)?

<!-- On a scale from 1-5 where 5 is the most important how would you rate it? -->

## Other Comments

<!-- Any other information you think could be helpful -->


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature Request 💡
about: Suggest a new idea for the project.
labels: enhancement
---

<!-- If you don't mind add a fun gif or meme, but no pressure -->

![A GIF or MEME to give some spice of the internet](url)

## Summary

Brief explanation of the feature.

### Basic example

If the proposal involves a new or changed API, include a basic code example. Omit this section if it's not applicable.

### Motivation

Why are we doing this? What use cases does it support? What is the expected outcome?


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!--- Provide a general summary of your changes in the Title above -->

<!-- If you don't mind add a fun gif or meme, but no pressure -->
![A GIF or MEME to give some spice of the internet](url)

## Description
<!--- Describe your changes in detail -->

## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->
[issue](https://github.com/BrainJS/brain.js/issues/###)

## How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, tests ran to see how -->
<!--- your change affects other areas of the code, etc. -->

## Screenshots (if appropriate):

## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)

## Author's Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] My code focuses on the main motivation and avoids scope creep.
- [ ] My code passes current tests and adds new tests where possible.
- [ ] My code is [SOLID](https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)) and [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself).
- [ ] I have updated the documentation as needed.

## Reviewer's Checklist:
- [ ] I kept my comments to the author positive, specific, and productive.
- [ ] I tested the code and didn't find any new problems.
- [ ] I think the motivation is good for the project.
- [ ] I think the code works to satisfies the motivation.


================================================
FILE: .github/workflows/nodejs.yml
================================================
name: CI
on:
  pull_request:
    branches: [master]
  push:
    branches: [master]
jobs:
  build:
    name: Build, lint, and test on Node ${{ matrix.node }} and ${{ matrix.os }}

    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        node: [18.x, 20.x, 22.x]
        os: [ubuntu-latest, windows-latest, macOS-latest]
        exclude:  # TODO: Get macOS tests to pass by upgrading to https://github.com/nodejs/node-gyp/releases
        - os: macos-latest
          node: 18.x
        - os: macos-latest
          node: 20.x
        - os: macos-latest
          node: 22.x

    steps:
      - name: Checkout repo
        uses: actions/checkout@v3

      - name: Use Node ${{ matrix.node }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node }}
          cache: npm

      - if: startsWith(matrix.os, 'ubuntu')
        run: |
          sudo apt-get install -y build-essential libglew-dev libglu1-mesa-dev libxi-dev pkg-config
          ls /usr/include/c++/
          # export CXXFLAGS='-include /usr/include/c++/11.2.0/limits'

      - name: Install deps and build (with cache)
        run: |
          touch ./dist
          rm package-lock.json
          npm i

      - name: Lint
        run: npm run lint

      - name: Test
        run: |
          # TODO: Remove the disabling of the following files.
          mv src/neural-network-gpu.end-to-end.test.ts src/neural-network-gpu.end-to-end.test.ts.DISABLED
          mv src/neural-network.bitwise.test.ts src/neural-network.bitwise.test.ts.DISABLED
          mv src/neural-network.trainopts.test.ts src/neural-network.trainopts.test.ts.DISABLED
          mv src/recurrent.end-to-end.test.ts src/recurrent.end-to-end.test.ts.DISABLED
          mv src/recurrent/gru.test.ts src/recurrent/gru.test.ts.DISABLED
          mv src/recurrent/lstm-time-step.end-to-end.test.ts src/recurrent/lstm-time-step.end-to-end.test.ts.DISABLED
          mv src/recurrent/lstm.test.ts src/recurrent/lstm.test.ts.DISABLED
          mv src/recurrent/rnn-time-step.test.ts src/recurrent/rnn-time-step.test.ts.DISABLED
          mv src/recurrent/rnn.test.ts src/recurrent/rnn.test.ts.DISABLED
          npm run test --ci --coverage --maxWorkers=2

      - name: Build
        run: npm run build

      - name: Codecov
        uses: codecov/codecov-action@v3


================================================
FILE: .gitignore
================================================
# Mac.
.DS_STORE
**.DS_STORE

# Node.
node_modules
npm-debug.log

# Yarn
yarn.lock
yarn-error.log

# parcel bundler cache
.cache

# code coverage
__coverage__

# webstorm
.idea

# distribution
dist


================================================
FILE: .npmignore
================================================
# Mac.
.DS_STORE
**.DS_STORE

# Node.
node_modules
npm-debug.log

# parcel bundler cache
.cache

# code coverage
__coverage__

test*.*

.editorconfig
.jshintrc
.travis.yml
.istanbul.yml
.babelrc
.idea/
.vscode/
test/
coverage/
.github/
.cache/
__coverage__
.cache
.dist/


================================================
FILE: .npmrc
================================================
registry=https://registry.npmjs.org

================================================
FILE: .nvmrc
================================================
v22.4.3


================================================
FILE: .prettierrc
================================================
{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "es5"
}


================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
  - '10'
  - '12'
cache:
  directories:
    - node_modules
install:
  - npm ci
script:
  - npm run lint
  - npm run build
  - npm run test
sudo: false


================================================
FILE: .vscode/launch.json
================================================
{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Launch via NPM",
      "request": "launch",
      "runtimeArgs": ["run-script", "debug"],
      "runtimeExecutable": "npm",
      "skipFiles": ["<node_internals>/**"],
      "type": "pwa-node"
    },
    {
      "type": "node",
      "name": "vscode-jest-tests",
      "request": "launch",
      "args": ["--runInBand"],
      "cwd": "${workspaceFolder}",
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen",
      "disableOptimisticBPs": true,
      "program": "${workspaceFolder}/node_modules/jest/bin/jest"
    }
  ]
}


================================================
FILE: LICENSE
================================================
Copyright (c) 2010-2019 Heather Arthur

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.



================================================
FILE: README.md
================================================
<p align="center">
  <img src="https://cdn.rawgit.com/harthur-org/brain.js/ff595242/logo.svg" alt="Logo" width=200px/>
</p>

# brain.js

GPU accelerated Neural networks in JavaScript for Browsers and Node.js

<p style="text-align: center" align="center">

  <a href="https://brain.js.org"><img src="https://img.shields.io/website?up_message=brain.js.org&url=https%3A%2F%2Fbrain.js.org" alt="GitHub"></a>
  [![npm](https://img.shields.io/npm/dt/brain.js.svg?style=flat-square)](https://npmjs.com/package/brain.js) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com)
  [![Backers on Open Collective](https://opencollective.com/brainjs/backers/badge.svg)](#backers)
  [![Sponsors on Open Collective](https://opencollective.com/brainjs/sponsors/badge.svg)](#sponsors)
  [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/brain-js/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
  [![Slack](https://slack.bri.im/badge.svg)](https://slack.bri.im)
  ![CI](https://github.com/BrainJS/brain.js/workflows/CI/badge.svg)
  [![codecov](https://codecov.io/gh/BrainJS/brain.js/branch/master/graph/badge.svg?token=3SJIBJ1679)](https://codecov.io/gh/BrainJS/brain.js)
  <a href="https://twitter.com/brainjsfnd"><img src="https://img.shields.io/twitter/follow/brainjsfnd?label=Twitter&style=social" alt="Twitter"></a>

  [![NPM](https://nodei.co/npm/brain.js.png?compact=true)](https://nodei.co/npm/brain.js/)

</p>

## About

`brain.js` is a GPU accelerated library for [Neural Networks](http://en.wikipedia.org/wiki/Artificial_neural_network) written in JavaScript.

:bulb: This is a continuation of the [**harthur/brain**](https://github.com/harthur/brain), which is not maintained anymore. [More info](https://github.com/harthur/brain/issues/72)

## Table of Contents

- [Installation and Usage](#Installation-and-Usage)
  - [NPM](#NPM)
  - [CDN](#CDN)
  - [Download](#Download)
  - [Installation note](#Installation-note)
  - [Building from source](#Building-from-source)
- [Examples](#examples)
  - [More Examples](#more-examples)
- [Training](#training)
  - [Data format](#data-format)
    - [For training with NeuralNetwork](#for-training-with-neuralnetwork)
    - [For training with `RNNTimeStep`, `LSTMTimeStep` and `GRUTimeStep`](#for-training-with-rnntimestep-lstmtimestep-and-grutimestep)
    - [For training with `RNN`, `LSTM` and `GRU`](#for-training-with-rnn-lstm-and-gru)
    - [For training with `AE`](#for-training-with-ae)
  - [Training Options](#training-options)
  - [Async Training](#async-training)
  - [Cross Validation](#cross-validation)
  - [Train Stream](#streams)
- [Methods](#methods)
  - [train](#traintrainingdata---trainingstatus)
  - [run](#runinput---prediction)
  - [forecast](#forecastinput-count---predictions)
- [Failing](#failing)
- [JSON](#json)
- [Standalone Function](#standalone-function)
- [Options](#options)
  - [activation](#activation)
  - [hiddenLayers](#hiddenlayers)
- [Streams](#streams)
- [Utilities](#utilities)
  - [`likely`](#likely)
  - [`toSVG`](#toSVG)
- [Neural Network Types](#neural-network-types)
  - [Why different Neural Network Types?](#why-different-neural-network-types)

## Installation and Usage

### NPM

If you can install `brain.js` with [npm](http://npmjs.org):

```bash
npm install brain.js
```

### CDN

```html
<script src="//unpkg.com/brain.js"></script>
```

### Download

[Download the latest brain.js for browser](https://unpkg.com/brain.js)

### Installation note

`Brain.js` depends on a native module [`headless-gl`](https://www.npmjs.com/package/headless-gl) for GPU support. In most cases installing `brain.js` from npm should just work. However, if you run into problems, this means prebuilt binaries are not able to download from GitHub repositories and you might need to build it yourself.

#### Building from source

Please make sure the following dependencies are installed and up to date and then run:

```bash
npm rebuild
```

##### System dependencies

###### Mac OS X

- [A supported version of Python](https://devguide.python.org/versions)
- [XCode](https://developer.apple.com/xcode/)

###### Ubuntu/Debian

- [A supported version of Python](https://devguide.python.org/versions)
- A GNU C++ environment (available via the `build-essential` package on `apt`)
- [libxi-dev](http://www.x.org/wiki/)
- Working and up-to-date OpenGL drivers
- [GLEW](http://glew.sourceforge.net/)
- [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/)

```bash
sudo apt-get install -y build-essential libglew-dev libglu1-mesa-dev libxi-dev pkg-config
```

###### Windows

- [A supported version of Python](https://devguide.python.org/versions)  __See:__ https://apps.microsoft.com/store/search/python
- [Microsoft Visual Studio Build Tools 2022](https://visualstudio.microsoft.com/downloads)
- run in cmd: `npm config set msvs_version 2022`  __Note: This no longer works in modern versions of npm.__
- run in cmd: `npm config set python python3`  __Note: This no longer works in modern versions of npm.__

\* If you are using `Build Tools 2017` then run `npm config set msvs_version 2017`  __Note: This no longer works in modern versions of npm.__

## Examples

Here's an example showcasing how to approximate the XOR function using `brain.js`:
more info on config [here](https://github.com/BrainJS/brain.js/blob/develop/src/neural-network.js#L31).

:bulb: [A fun and practical introduction to Brain.js](https://scrimba.com/g/gneuralnetworks)

```javascript
// provide optional config object (or undefined). Defaults shown.
const config = {
  binaryThresh: 0.5,
  hiddenLayers: [3], // array of ints for the sizes of the hidden layers in the network
  activation: 'sigmoid', // supported activation types: ['sigmoid', 'relu', 'leaky-relu', 'tanh'],
  leakyReluAlpha: 0.01, // supported for activation type 'leaky-relu'
};

// create a simple feed-forward neural network with backpropagation
const net = new brain.NeuralNetwork(config);

net.train([
  { input: [0, 0], output: [0] },
  { input: [0, 1], output: [1] },
  { input: [1, 0], output: [1] },
  { input: [1, 1], output: [0] },
]);

const output = net.run([1, 0]); // [0.987]
```

or
more info on config [here](https://github.com/BrainJS/brain.js/blob/develop/src/recurrent/rnn.js#L726).

```javascript
// provide optional config object, defaults shown.
const config = {
  inputSize: 20,
  inputRange: 20,
  hiddenLayers: [20, 20],
  outputSize: 20,
  learningRate: 0.01,
  decayRate: 0.999,
};

// create a simple recurrent neural network
const net = new brain.recurrent.RNN(config);

net.train([
  { input: [0, 0], output: [0] },
  { input: [0, 1], output: [1] },
  { input: [1, 0], output: [1] },
  { input: [1, 1], output: [0] },
]);

let output = net.run([0, 0]); // [0]
output = net.run([0, 1]); // [1]
output = net.run([1, 0]); // [1]
output = net.run([1, 1]); // [0]
```

However, there is no reason to use a neural network to figure out XOR. (-: So, here is a more involved, realistic example:
[Demo: training a neural network to recognize color contrast](https://brain.js.org/).

## More Examples

[Brain.js Examples Repo](https://github.com/BrainJS/brain.js-examples)

You can check out this fantastic screencast, which explains how to train a simple neural network using a real-world dataset: [How to create a neural network in the browser using Brain.js](https://scrimba.com/c/c36zkcb).

## Training

Use `train()` to train the network with an array of training data. The network has to be trained with all the data in bulk in one call to `train()`. More training patterns will probably take longer to train, but will usually result in a network better at classifying new patterns.

### Note

Training is computationally expensive, so you should try to train the network offline (or on a Worker) and use the `toFunction()` or `toJSON()` options to plug the pre-trained network into your website.

### Data format

#### For training with `NeuralNetwork`

Each training pattern should have an `input` and an `output`, both of which can be either an array of numbers from `0` to `1` or a hash of numbers from `0` to `1`. For the [color contrast demo](https://brain.js.org/) it looks something like this:

```javascript
const net = new brain.NeuralNetwork();

net.train([
  { input: { r: 0.03, g: 0.7, b: 0.5 }, output: { black: 1 } },
  { input: { r: 0.16, g: 0.09, b: 0.2 }, output: { white: 1 } },
  { input: { r: 0.5, g: 0.5, b: 1.0 }, output: { white: 1 } },
]);

const output = net.run({ r: 1, g: 0.4, b: 0 }); // { white: 0.99, black: 0.002 }
```

Here's another variation of the above example. (_Note_ that input objects do not need to be similar.)

```javascript
net.train([
  { input: { r: 0.03, g: 0.7 }, output: { black: 1 } },
  { input: { r: 0.16, b: 0.2 }, output: { white: 1 } },
  { input: { r: 0.5, g: 0.5, b: 1.0 }, output: { white: 1 } },
]);

const output = net.run({ r: 1, g: 0.4, b: 0 }); // { white: 0.81, black: 0.18 }
```

#### For training with `RNNTimeStep`, `LSTMTimeStep` and `GRUTimeStep`

Each training pattern can either:

- Be an array of numbers
- Be an array of arrays of numbers

Example using an array of numbers:

```javascript
const net = new brain.recurrent.LSTMTimeStep();

net.train([[1, 2, 3]]);

const output = net.run([1, 2]); // 3
```

Example using an array of arrays of numbers:

```javascript
const net = new brain.recurrent.LSTMTimeStep({
  inputSize: 2,
  hiddenLayers: [10],
  outputSize: 2,
});

net.train([
  [1, 3],
  [2, 2],
  [3, 1],
]);

const output = net.run([
  [1, 3],
  [2, 2],
]); // [3, 1]
```

#### For training with `RNN`, `LSTM` and `GRU`

Each training pattern can either:

- Be an array of values
- Be a string
- Have an `input` and an `output`
  - Either of which can have an array of values or a string

CAUTION: When using an array of values, you can use ANY value, however, the values are represented in the neural network by a single input. So the more _distinct values_ has _the larger your input layer_. If you have a hundreds, thousands, or millions of floating point values _THIS IS NOT THE RIGHT CLASS FOR THE JOB_. Also, when deviating from strings, this gets into beta

Example using direct strings:
Hello World Using Brainjs
```javascript

  const net = new brain.recurrent.LSTM();

  net.train(['I am brainjs, Hello World!']);

  const output = net.run('I am brainjs');
  alert(output);
```

```javascript
const net = new brain.recurrent.LSTM();

net.train([
  'doe, a deer, a female deer',
  'ray, a drop of golden sun',
  'me, a name I call myself',
]);

const output = net.run('doe'); // ', a deer, a female deer'
```

Example using strings with inputs and outputs:

```javascript
const net = new brain.recurrent.LSTM();

net.train([
  { input: 'I feel great about the world!', output: 'happy' },
  { input: 'The world is a terrible place!', output: 'sad' },
]);

const output = net.run('I feel great about the world!'); // 'happy'
```

#### For training with `AE`

Each training pattern can either:

- Be an array of numbers
- Be an array of arrays of numbers

Training an autoencoder to compress the values of a XOR calculation:

```javascript
const net = new brain.AE(
  {
    hiddenLayers: [ 5, 2, 5 ]
  }
);

net.train([
  [ 0, 0, 0 ],
  [ 0, 1, 1 ],
  [ 1, 0, 1 ],
  [ 1, 1, 0 ]
]);
```

Encoding/decoding:

```javascript
const input = [ 0, 1, 1 ];

const encoded = net.encode(input);
const decoded = net.decode(encoded);
```

Denoise noisy data:

```javascript
const noisyData = [ 0, 1, 0 ];

const data = net.denoise(noisyData);
```

Test for anomalies in data samples:

```javascript
const shouldBeFalse = net.includesAnomalies([0, 1, 1]);
const shouldBeTrue = net.includesAnomalies([0, 1, 0]);
```

### Training Options

`train()` takes a hash of options as its second argument:

```javascript
net.train(data, {
  // Defaults values --> expected validation
  iterations: 20000, // the maximum times to iterate the training data --> number greater than 0
  errorThresh: 0.005, // the acceptable error percentage from training data --> number between 0 and 1
  log: false, // true to use console.log, when a function is supplied it is used --> Either true or a function
  logPeriod: 10, // iterations between logging out --> number greater than 0
  learningRate: 0.3, // scales with delta to effect training rate --> number between 0 and 1
  momentum: 0.1, // scales with next layer's change value --> number between 0 and 1
  callback: null, // a periodic call back that can be triggered while training --> null or function
  callbackPeriod: 10, // the number of iterations through the training data between callback calls --> number greater than 0
  timeout: number, // the max number of milliseconds to train for --> number greater than 0. Default --> Infinity
});
```

The network will stop training whenever one of the two criteria is met: the training error has gone below the threshold (default `0.005`), or the max number of iterations (default `20000`) has been reached.

By default, training will not let you know how it's doing until the end, but set `log` to `true` to get periodic updates on the current training error of the network. The training error should decrease every time. The updates will be printed to the console. If you set `log` to a function, this function will be called with the updates instead of printing to the console.
However, if you want to use the values of the updates in your own output, the `callback` can be set to a function to do so instead.

The learning rate is a parameter that influences how quickly the network trains. It's a number from `0` to `1`. If the learning rate is close to `0`, it will take longer to train. If the learning rate is closer to `1`, it will train faster, but training results may be constrained to a local minimum and perform badly on new data.(_Overfitting_) The default learning rate is `0.3`.

The momentum is similar to learning rate, expecting a value from `0` to `1` as well, but it is multiplied against the next level's change value. The default value is `0.1`

Any of these training options can be passed into the constructor or passed into the `updateTrainingOptions(opts)` method and they will be saved on the network and used during the training time. If you save your network to json, these training options are saved and restored as well (except for callback and log, callback will be forgotten and log will be restored using console.log).

A boolean property called `invalidTrainOptsShouldThrow` is set to `true` by default. While the option is `true`, if you enter a training option that is outside the normal range, an error will be thrown with a message about the abnormal option. When the option is set to `false`, no error will be sent, but a message will still be sent to `console.warn` with the related information.

### Async Training

`trainAsync()` takes the same arguments as train (data and options). Instead of returning the results object from training, it returns a promise that when resolved will return the training results object.  Does NOT work with:
* `brain.recurrent.RNN`
* `brain.recurrent.GRU`
* `brain.recurrent.LSTM`
* `brain.recurrent.RNNTimeStep`
* `brain.recurrent.GRUTimeStep`
* `brain.recurrent.LSTMTimeStep`

```javascript
const net = new brain.NeuralNetwork();
net
  .trainAsync(data, options)
  .then((res) => {
    // do something with my trained network
  })
  .catch(handleError);
```

With multiple networks you can train in parallel like this:

```javascript
const net = new brain.NeuralNetwork();
const net2 = new brain.NeuralNetwork();

const p1 = net.trainAsync(data, options);
const p2 = net2.trainAsync(data, options);

Promise.all([p1, p2])
  .then((values) => {
    const res = values[0];
    const res2 = values[1];
    console.log(
      `net trained in ${res.iterations} and net2 trained in ${res2.iterations}`
    );
    // do something super cool with my 2 trained networks
  })
  .catch(handleError);
```

### Cross Validation

[Cross Validation](<https://en.wikipedia.org/wiki/Cross-validation_(statistics)>) can provide a less fragile way of training on larger data sets. The brain.js api provides Cross Validation in this example:

```js
const crossValidate = new brain.CrossValidate(() => new brain.NeuralNetwork(networkOptions));
crossValidate.train(data, trainingOptions, k); //note k (or KFolds) is optional
const json = crossValidate.toJSON(); // all stats in json as well as neural networks
const net = crossValidate.toNeuralNetwork(); // get top performing net out of `crossValidate`

// optionally later
const json = crossValidate.toJSON();
const net = crossValidate.fromJSON(json);
```

Use `CrossValidate` with these classes:

- `brain.NeuralNetwork`
- `brain.RNNTimeStep`
- `brain.LSTMTimeStep`
- `brain.GRUTimeStep`

An example of using cross validate can be found in [cross-validate.ts](https://github.com/BrainJS/brain.js-examples/blob/main/src/cross-validate.ts)

## Methods

### `train(trainingData)` -> trainingStatus

The output of `train()` is a hash of information about how the training went:

```javascript
{
  error: 0.0039139985510105032,  // training error
  iterations: 406                // training iterations
}
```

### `run(input)` -> prediction

Supported on classes:

- `brain.NeuralNetwork`
- `brain.NeuralNetworkGPU` -> All the functionality of `brain.NeuralNetwork` but, ran on GPU (via gpu.js in WebGL2, WebGL1, or fallback to CPU)
- `brain.recurrent.RNN`
- `brain.recurrent.LSTM`
- `brain.recurrent.GRU`
- `brain.recurrent.RNNTimeStep`
- `brain.recurrent.LSTMTimeStep`
- `brain.recurrent.GRUTimeStep`

Example:

```js
// feed forward
const net = new brain.NeuralNetwork();
net.fromJSON(json);
net.run(input);

// time step
const net = new brain.LSTMTimeStep();
net.fromJSON(json);
net.run(input);

// recurrent
const net = new brain.LSTM();
net.fromJSON(json);
net.run(input);
```

### `forecast(input, count)` -> predictions

Available with the following classes. Outputs a array of predictions. Predictions being a continuation of the inputs.

- `brain.recurrent.RNNTimeStep`
- `brain.recurrent.LSTMTimeStep`
- `brain.recurrent.GRUTimeStep`

Example:

```js
const net = new brain.LSTMTimeStep();
net.fromJSON(json);
net.forecast(input, 3);
```

### `toJSON() -> json`

Serialize neural network to json

### `fromJSON(json)`

Deserialize neural network from json

## Failing

If the network failed to train, the error will be above the error threshold. This could happen if the training data is too noisy (most likely), the network does not have enough hidden layers or nodes to handle the complexity of the data, or it has not been trained for enough iterations.

If the training error is still something huge like `0.4` after 20000 iterations, it's a good sign that the network can't make sense of the given data.

### RNN, LSTM, or GRU Output too short or too long

The instance of the net's property `maxPredictionLength` (default 100) can be set to adjust the output of the net;

Example:

```js
const net = new brain.recurrent.LSTM();

// later in code, after training on a few novels, write me a new one!
net.maxPredictionLength = 1000000000; // Be careful!
net.run('Once upon a time');
```

## JSON

Serialize or load in the state of a trained network with JSON:

```javascript
const json = net.toJSON();
net.fromJSON(json);
```

## Standalone Function

You can also get a custom standalone function from a trained network that acts just like `run()`:

```javascript
const run = net.toFunction();
const output = run({ r: 1, g: 0.4, b: 0 });
console.log(run.toString()); // copy and paste! no need to import brain.js
```

## Options

`NeuralNetwork()` takes a hash of options:

```javascript
const net = new brain.NeuralNetwork({
  activation: 'sigmoid', // activation function
  hiddenLayers: [4],
  learningRate: 0.6, // global learning rate, useful when training using streams
});
```

### activation

This parameter lets you specify which activation function your neural network should use. There are currently four supported activation functions, **sigmoid** being the default:

- [sigmoid](https://www.wikiwand.com/en/Sigmoid_function)
- [relu](<https://www.wikiwand.com/en/Rectifier_(neural_networks)>)
- [leaky-relu](<https://www.wikiwand.com/en/Rectifier_(neural_networks)>)
  - related option - 'leakyReluAlpha' optional number, defaults to 0.01
- [tanh](https://theclevermachine.wordpress.com/tag/tanh-function/)

Here's a table (thanks, Wikipedia!) summarizing a plethora of activation functions — [Activation Function](https://www.wikiwand.com/en/Activation_function)

### hiddenLayers

You can use this to specify the number of hidden layers in the network and the size of each layer. For example, if you want two hidden layers - the first with 3 nodes and the second with 4 nodes, you'd give:

```js
hiddenLayers: [3, 4];
```

By default `brain.js` uses one hidden layer with size proportionate to the size of the input array.

## Streams

Use https://www.npmjs.com/package/train-stream to stream data to a NeuralNetwork

## Utilities

### `likely`

```js
const likely = require('brain/likely');
const key = likely(input, net);
```

Likely example see: [simple letter detection](https://github.com/BrainJS/brain.js-examples/blob/main/src/which-letter-simple.ts)

### `toSVG`

```js
<script src="../../src/utilities/svg.js"></script>
```

Renders the network topology of a feedforward network

```js
document.getElementById('result').innerHTML = brain.utilities.toSVG(
  network,
  options
);
```

toSVG example see: [network rendering](https://github.com/BrainJS/brain.js-examples/blob/main/src/rendering-svg/index.html)

The user interface used:
![screenshot1](https://user-images.githubusercontent.com/43925925/48969024-e526ed80-f000-11e8-85bd-e10967cfaee2.png)

## Neural Network Types

- [`brain.NeuralNetwork`](src/neural-network.ts) - [Feedforward Neural Network](https://en.wikipedia.org/wiki/Feedforward_neural_network) with backpropagation
- [`brain.NeuralNetworkGPU`](src/neural-network-gpu.ts) - [Feedforward Neural Network](https://en.wikipedia.org/wiki/Feedforward_neural_network) with backpropagation, GPU version
- [`brain.AE`](src/autoencoder.ts) - [Autoencoder or "AE"](https://en.wikipedia.org/wiki/Autoencoder) with backpropogation and GPU support
- [`brain.recurrent.RNNTimeStep`](src/recurrent/rnn-time-step.ts) - [Time Step Recurrent Neural Network or "RNN"](https://en.wikipedia.org/wiki/Recurrent_neural_network)
- [`brain.recurrent.LSTMTimeStep`](src/recurrent/lstm-time-step.ts) - [Time Step Long Short Term Memory Neural Network or "LSTM"](https://en.wikipedia.org/wiki/Long_short-term_memory)
- [`brain.recurrent.GRUTimeStep`](src/recurrent/gru-time-step.ts) - [Time Step Gated Recurrent Unit or "GRU"](https://en.wikipedia.org/wiki/Gated_recurrent_unit)
- [`brain.recurrent.RNN`](src/recurrent/rnn.ts) - [Recurrent Neural Network or "RNN"](https://en.wikipedia.org/wiki/Recurrent_neural_network)
- [`brain.recurrent.LSTM`](src/recurrent/lstm.ts) - [Long Short Term Memory Neural Network or "LSTM"](https://en.wikipedia.org/wiki/Long_short-term_memory)
- [`brain.recurrent.GRU`](src/recurrent/gru.ts) - [Gated Recurrent Unit or "GRU"](https://en.wikipedia.org/wiki/Gated_recurrent_unit)
- [`brain.FeedForward`](src/feed-forward.ts) - [Highly Customizable Feedforward Neural Network](https://en.wikipedia.org/wiki/Feedforward_neural_network) with backpropagation
- [`brain.Recurrent`](src/recurrent.ts) - [Highly Customizable Recurrent Neural Network](https://en.wikipedia.org/wiki/Recurrent_neural_network) with backpropagation

### Why different Neural Network Types

Different neural nets do different things well. For example:

- A Feedforward Neural Network can classify simple things very well, but it has no memory of previous actions and has infinite variation of results.
- A Time Step Recurrent Neural Network _remembers_, and can predict future values.
- A Recurrent Neural Network _remembers_, and has a finite set of results.

## Get Involved

### W3C machine learning standardization process

If you are a developer or if you just care about how ML API should look like - please take a part and join W3C community and share your opinions or simply support opinions you like or agree with.

Brain.js is a widely adopted open source machine learning library in the javascript world. There are several reasons for it, but most notable is **simplicity of usage while not sacrificing performance**.
We would like to keep it also simple to learn, simple to use and performant when it comes to W3C standard. We think that current brain.js API is quite close to what we could expect to become a standard.
And since supporting doesn't require much effort and still can make a huge difference feel free to join W3C community group and support us with brain.js like API.

Get involved into W3C machine learning ongoing standardization process [here](https://www.w3.org/community/webmachinelearning/).
You can also join our open discussion about standardization [here](https://github.com/BrainJS/brain.js/issues/337).

## Issues

If you have an issue, either a bug or a feature you think would benefit your project let us know and we will do our best.

Create issues [here](https://github.com/BrainJS/brain.js/issues) and follow the template.

### brain.js.org

Source for `brain.js.org` is available at [Brain.js.org Repository](https://github.com/BrainJS/brain.js.org). Built using awesome `vue.js` & `bulma`. Contributions are always welcome.

## Contributors

This project exists thanks to all the people who contribute. [[Contribute](/.github/CONTRIBUTING.md)].
<a href="https://github.com/BrainJS/brain.js/graphs/contributors"><img src="https://opencollective.com/brainjs/contributors.svg?width=890&button=false" /></a>

## Backers

Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/brainjs#backer)]

<a href="https://opencollective.com/brainjs#backers" target="_blank"><img src="https://opencollective.com/brainjs/backers.svg?width=890"></a>

## Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/brainjs#sponsor)]

<a href="https://opencollective.com/brainjs/sponsor/0/website" target="_blank"><img src="https://opencollective.com/brainjs/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/brainjs/sponsor/1/website" target="_blank"><img src="https://opencollective.com/brainjs/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/brainjs/sponsor/2/website" target="_blank"><img src="https://opencollective.com/brainjs/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/brainjs/sponsor/3/website" target="_blank"><img src="https://opencollective.com/brainjs/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/brainjs/sponsor/4/website" target="_blank"><img src="https://opencollective.com/brainjs/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/brainjs/sponsor/5/website" target="_blank"><img src="https://opencollective.com/brainjs/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/brainjs/sponsor/6/website" target="_blank"><img src="https://opencollective.com/brainjs/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/brainjs/sponsor/7/website" target="_blank"><img src="https://opencollective.com/brainjs/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/brainjs/sponsor/8/website" target="_blank"><img src="https://opencollective.com/brainjs/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/brainjs/sponsor/9/website" target="_blank"><img src="https://opencollective.com/brainjs/sponsor/9/avatar.svg"></a>


================================================
FILE: jest.config.json
================================================
{
  "collectCoverage": false,
  "collectCoverageFrom": ["src/**/*"],
  "coverageDirectory": "__coverage__",
  "coverageProvider": "v8",
  "globalSetup": "",
  "globals": {
    "ts-jest": {
      "tsconfig": "tsconfig.json"
    }
  },
  "preset": "ts-jest/presets/js-with-ts",
  "setupFiles": [],
  "testEnvironment": "node"
}


================================================
FILE: package.json
================================================
{
  "author": "Heather Arthur <fayearthur@gmail.com>",
  "browser": "dist/browser.js",
  "bugs": {
    "url": "https://github.com/brainjs/brain.js/issues"
  },
  "description": "Neural networks in JavaScript",
  "dependencies": {
    "thaw.js": "^2.1.4"
  },
  "peerDependencies": {
    "gpu.js": "^2.16.0"
  },
  "devDependencies": {
    "rimraf": "^6.0.0",
    "@babel/preset-typescript": "^7.13.0",
    "@rollup/plugin-commonjs": "^15.1.0",
    "@rollup/plugin-json": "^4.1.0",
    "@rollup/plugin-node-resolve": "^9.0.0",
    "@rollup/plugin-typescript": "^8.2.0",
    "@types/eslint": "^7.2.4",
    "@types/eslint-plugin-prettier": "^3.1.0",
    "@types/jest": "^27.0.2",
    "@types/node": "^14.14.2",
    "@types/prettier": "^2.1.5",
    "@types/rollup-plugin-node-builtins": "^2.1.1",
    "@types/rollup-plugin-node-globals": "^1.4.0",
    "@typescript-eslint/eslint-plugin": "^4.5.0",
    "@typescript-eslint/parser": "^4.5.0",
    "acorn": "^8.0.4",
    "c8": "^7.3.4",
    "codecov": "^3.8.0",
    "del-cli": "^5.0.0",
    "eslint": "^7.12.0",
    "eslint-config-prettier": "^6.14.0",
    "eslint-config-standard": "^15.0.0",
    "eslint-config-standard-with-typescript": "^19.0.1",
    "eslint-plugin-import": "^2.22.1",
    "eslint-plugin-jest": "^24.1.0",
    "eslint-plugin-node": "^11.1.0",
    "eslint-plugin-prettier": "^3.1.4",
    "eslint-plugin-promise": "^4.2.1",
    "eslint-plugin-standard": "^4.0.2",
    "fast-xml-parser": "^3.17.4",
    "gpu-mock.js": "^1.3.1",
    "gpu.js": "^2.15.2",
    "husky": "^4.3.0",
    "jest": "^26.6.1",
    "npm-run-all": "^4.1.5",
    "prettier": "^2.1.2",
    "rollup": "^2.39.1",
    "rollup-plugin-terser": "^7.0.2",
    "ts-jest": "^26.4.2",
    "ts-node": "^9.1.1",
    "typescript": "^4.0.3"
  },
  "engines": {
    "node": ">=8.6.x"
  },
  "files": [
    "dist/"
  ],
  "homepage": "https://github.com/brainjs/brain.js#readme",
  "husky": {
    "hooks": {
      "pre-commit": "npm run lint"
    }
  },
  "keywords": [
    "ai",
    "artificial-intelligence",
    "brain",
    "brainjs",
    "brain.js",
    "feed forward",
    "neural network",
    "classifier",
    "neural",
    "network",
    "neural-networks",
    "machine-learning",
    "synapse",
    "recurrent",
    "long short term memory",
    "gated recurrent unit",
    "time series",
    "time step",
    "prediction",
    "rnn",
    "lstm",
    "gru"
  ],
  "license": "MIT",
  "main": "dist/index.js",
  "name": "brain.js",
  "repository": {
    "type": "git",
    "url": "git+ssh://git@github.com/brainjs/brain.js.git"
  },
  "scripts": {
    "build": "run-p build:**",
    "build:browser": "rollup -c rollup.config.browser.js",
    "build:node": "rollup -c rollup.config.js",
    "build:ts": "tsc --declaration --emitDeclarationOnly --declarationMap",
    "coverage": "jest --coverage --coverage-provider v8 && codecov",
    "dist": "npm run build",
    "lint": "run-p lint:**",
    "lint:eslint": "eslint --fix --ext .js,.ts src",
    "lint:typecheck": "tsc --noEmit",
    "test": "jest",
    "watch": "run-p watch:**",
    "watch:node": "rollup -c rollup.config.js -w",
    "watch:test": "jest --watch",
    "clean": "rimraf ./dist",
    "prepare": "npm run clean && npm run build"
  },
  "types": "dist/",
  "unpkg": "dist/browser.js",
  "version": "2.0.0-beta.24"
}


================================================
FILE: rollup.config.browser.js
================================================
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import resolve from '@rollup/plugin-node-resolve';
import typescript from '@rollup/plugin-typescript';
import { fileURLToPath } from 'node:url';

const name = 'brain';
const extensions = ['.js', '.json', '.node', '.ts'];
const file = 'dist/browser.js';

export default {
  input: './src/index.ts',

  // Specify here external modules which you don't want to include in your bundle (for instance: 'lodash', 'moment' etc.)
  // https://rollupjs.org/guide/en#external-e-external
  external: [
    // brain js already uses gpu.js as peer dependencies so it shouldn't be like this
    fileURLToPath(
      new URL('./node_modules/gpu.js/src/index.js', import.meta.url)
    ),
  ],

  plugins: [
    // Allows node_modules resolution
    resolve({
      preferBuiltins: false,
      browser: true,
      extensions,
    }),

    // allow json importing
    json(),

    // Allow bundling cjs modules. Rollup doesn't understand cjs
    commonjs(),

    // Compile TypeScript/JavaScript files
    typescript(),
  ],
  output: [
    {
      file,
      format: 'umd',
      sourcemap: true,
      globals: {
        'gpu.js': `GPU`,
      },
      name,
    },
  ],
};


================================================
FILE: rollup.config.js
================================================
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import resolve from '@rollup/plugin-node-resolve';
import typescript from '@rollup/plugin-typescript';
import * as pkg from './package.json';

const extensions = ['.js', '.json', '.node', '.ts'];

export default {
  input: './src/index.ts',

  // Specify here external modules which you don't want to include in your bundle (for instance: 'lodash', 'moment' etc.)
  // https://rollupjs.org/guide/en#external-e-external
  external: [
    'gpu.js'
  ],

  plugins: [
    // Allows node_modules resolution
    resolve({
      preferBuiltins: true,
      browser: false,
      extensions,
    }),

    // allow json importing
    json(),

    // Allow bundling cjs modules. Rollup doesn't understand cjs
    commonjs(),

    // Compile TypeScript/JavaScript files
    typescript(),
  ],

  output: [
    {
      file: pkg.main,
      format: 'cjs',
      sourcemap: true,
    },
  ],
};


================================================
FILE: src/README.md
================================================
# Brain.js Source Files
Welcome!

Brain.js aims to be:
* Javascript and Node based
* an easy, well thought out api
* simple enough to teach a child
* performant enough for enterprise and hobbyists alike

The industry wants you to think neural networks are complex, they are not. 

================================================
FILE: src/activation/README.md
================================================
# [Activation](https://en.wikipedia.org/wiki/Activation_function)
## What is activation?
Activation can be thought of as a sort of one way compressor for numerical values.

Activation is useful for:
* representing a wide array of numbers in a very small number
* throwing away values the network does not care about
* focusing on values the network does care about

For every activation, there is usually a widely accepted means of measuring it, this term
the activation's derivative function.  For simplicity, it is referred to as our "measure"
function.


## Programming structure
### Each activation has at least the following two exported functions 
* `activate` - the activation type's mathematical function
  * The term `activate` specifically correlates to the activation function type
* `measure` - the derivative to measure the activation

For programmatic, simplicity, and practicality, we namespace the structure as:
```
relu (activation type)
 |-activate
 |-measure
```


================================================
FILE: src/activation/index.test.ts
================================================
import * as activation from '../activation';
import * as leakyRelu from '../activation/leaky-relu';
import * as relu from '../activation/relu';
import * as sigmoid from '../activation/sigmoid';
import * as tanh from '../activation/tanh';

describe('activation', () => {
  test('it has all expected activations', () => {
    expect(activation.leakyRelu).toBe(leakyRelu);
    expect(activation.relu).toBe(relu);
    expect(activation.sigmoid).toBe(sigmoid);
    expect(activation.tanh).toBe(tanh);
  });
});


================================================
FILE: src/activation/index.ts
================================================
export * as relu from './relu';
export * as sigmoid from './sigmoid';
export * as tanh from './tanh';
export * as leakyRelu from './leaky-relu';


================================================
FILE: src/activation/leaky-relu.test.ts
================================================
import * as leakyRelu from './leaky-relu';

describe('leakyRelu', () => {
  describe('.active()', () => {
    describe('when weight is greater than 0', () => {
      it('returns weight', () => {
        expect(leakyRelu.activate(1)).toBe(1);
      });
    });

    describe('when value is equal to 0', () => {
      it('returns value * 0.01', () => {
        expect(leakyRelu.activate(0)).toBe(0);
      });
    });

    describe('when value is less than 0', () => {
      it('returns value * 0.01', () => {
        expect(leakyRelu.activate(-1)).toBe(-0.01);
      });
    });
  });
  describe('.measure()', () => {
    describe('when weight is greater than 0', () => {
      it('returns error', () => {
        const error = 0.1;
        expect(leakyRelu.measure(1, error)).toBe(error);
      });
    });
    describe('when weight is equal to 0', () => {
      it('returns error', () => {
        const error = 0.1;
        expect(leakyRelu.measure(1, error)).toBe(error);
      });
    });
    describe('when weight is less than 0', () => {
      it('returns error', () => {
        expect(leakyRelu.measure(-1, 1)).toBe(0.01);
      });
    });
  });
});


================================================
FILE: src/activation/leaky-relu.ts
================================================
/**
 * Leaky Relu Activation, aka Leaky Rectified Linear Unit Activation
 * @description https://en.wikipedia.org/wiki/Rectifier_(neural_networks)
 */
export function activate(weight: number): number {
  return weight > 0 ? weight : 0.01 * weight;
}

/**
 * Leaky Relu derivative
 */
export function measure(weight: number, error: number): number {
  return weight > 0 ? error : 0.01 * error;
}


================================================
FILE: src/activation/relu.test.ts
================================================
import * as relu from './relu';

describe('relu', () => {
  describe('.active()', () => {
    describe('when weight is greater than 0', () => {
      it('returns weight', () => {
        expect(relu.activate(99)).toBe(99);
      });
    });
    describe('when value is equal to 0', () => {
      it('returns 0', () => {
        expect(relu.activate(0)).toBe(0);
      });
    });
    describe('when value is less than 0', () => {
      it('returns 0', () => {
        expect(relu.activate(0)).toBe(0);
      });
    });
  });
  describe('.measure()', () => {
    describe('when weight is greater than 0', () => {
      it('returns error', () => {
        const error = 0.1;
        expect(relu.measure(1, error)).toBe(error);
      });
    });
    describe('when weight is equal to 0', () => {
      it('returns error', () => {
        const error = 0.1;
        expect(relu.measure(1, error)).toBe(error);
      });
    });
    describe('when weight is less than 0', () => {
      it('returns 0', () => {
        expect(relu.measure(-1, 1)).toBe(0);
      });
    });
  });
});


================================================
FILE: src/activation/relu.ts
================================================
/**
 * Relu Activation, aka Rectified Linear Unit Activation
 * @description https://en.wikipedia.org/wiki/Rectifier_(neural_networks)
 */
export function activate(weight: number): number {
  return Math.max(0, weight);
}

/**
 * Relu derivative
 */
export function measure(weight: number, delta: number): number {
  if (weight <= 0) {
    return 0;
  }
  return delta;
}


================================================
FILE: src/activation/sigmoid.test.ts
================================================
import * as sigmoid from './sigmoid';

describe('sigmoid', () => {
  describe('.active()', () => {
    it('matches for value 1', () => {
      expect(sigmoid.activate(1).toFixed(5)).toBe('0.73106');
    });
  });
  describe('.measure()', () => {
    it('matches for value .7', () => {
      expect(sigmoid.measure(0.7, 0.5).toFixed(5)).toBe('0.10500');
    });
  });
});


================================================
FILE: src/activation/sigmoid.ts
================================================
/**
 * sigmoid activation
 */
export function activate(value: number): number {
  return 1 / (1 + Math.exp(-value));
}

/**
 * sigmoid derivative
 */
export function measure(weight: number, error: number): number {
  return weight * (1 - weight) * error;
}


================================================
FILE: src/activation/tanh.test.ts
================================================
import * as tanh from './tanh';

describe('tanh', () => {
  describe('.active()', () => {
    it('matches for value 1', () => {
      expect(tanh.activate(1).toFixed(5)).toBe(Math.tanh(1).toFixed(5));
    });
  });
  describe('.measure()', () => {
    it('matches for value .7', () => {
      expect(tanh.measure(0.7, 0.5).toFixed(5)).toBe('0.25500');
    });
  });
});


================================================
FILE: src/activation/tanh.ts
================================================
/**
 * Hyperbolic tan
 */
export function activate(weight: number): number {
  return Math.tanh(weight);
}

/**
 * @description grad for z = tanh(x) is (1 - z^2)
 */
export function measure(weight: number, error: number): number {
  return (1 - weight * weight) * error;
}


================================================
FILE: src/autoencoder.test.ts
================================================
import AE from './autoencoder';

const trainingData = [
  [0, 0, 0],
  [0, 1, 1],
  [1, 0, 1],
  [1, 1, 0],
];

const xornet = new AE<number[], number[]>({
  decodedSize: 3,
  hiddenLayers: [5, 2, 5],
});

const errorThresh = 0.011;

const result = xornet.train(trainingData, {
  iterations: 100000,
  errorThresh,
});

test('denoise a data sample', async () => {
  expect(result.error).toBeLessThanOrEqual(errorThresh);

  function xor(...args: number[]) {
    return Math.round(xornet.denoise(args)[2]);
  }

  const run1 = xor(0, 0, 0);
  const run2 = xor(0, 1, 1);
  const run3 = xor(1, 0, 1);
  const run4 = xor(1, 1, 0);

  expect(run1).toBe(0);
  expect(run2).toBe(1);
  expect(run3).toBe(1);
  expect(run4).toBe(0);
});

test('encode and decode a data sample', async () => {
  expect(result.error).toBeLessThanOrEqual(errorThresh);

  const run1$input = [0, 0, 0];
  const run1$encoded = xornet.encode(run1$input);
  const run1$decoded = xornet.decode(run1$encoded);

  const run2$input = [0, 1, 1];
  const run2$encoded = xornet.encode(run2$input);
  const run2$decoded = xornet.decode(run2$encoded);

  for (let i = 0; i < 3; i++)
    expect(Math.round(run1$decoded[i])).toBe(run1$input[i]);
  for (let i = 0; i < 3; i++)
    expect(Math.round(run2$decoded[i])).toBe(run2$input[i]);
});

test('test a data sample for anomalies', async () => {
  expect(result.error).toBeLessThanOrEqual(errorThresh);

  function includesAnomalies(...args: number[]) {
    expect(xornet.likelyIncludesAnomalies(args)).toBe(false);
  }

  includesAnomalies(0, 0, 0);
  includesAnomalies(0, 1, 1);
  includesAnomalies(1, 0, 1);
  includesAnomalies(1, 1, 0);
});


================================================
FILE: src/autoencoder.ts
================================================
import { KernelOutput, Texture, TextureArrayOutput } from 'gpu.js';
import {
  IJSONLayer,
  INeuralNetworkData,
  INeuralNetworkDatum,
  INeuralNetworkTrainOptions,
} from './neural-network';
import {
  INeuralNetworkGPUOptions,
  NeuralNetworkGPU,
} from './neural-network-gpu';
import { INeuralNetworkState } from './neural-network-types';
import { UntrainedNeuralNetworkError } from './errors/untrained-neural-network-error';

export interface IAEOptions {
  binaryThresh: number;
  decodedSize: number;
  hiddenLayers: number[];
}

/**
 * An autoencoder learns to compress input data down to relevant features and reconstruct input data from its compressed representation.
 */
export class AE<
  DecodedData extends INeuralNetworkData,
  EncodedData extends INeuralNetworkData
> {
  private decoder?: NeuralNetworkGPU<EncodedData, DecodedData>;
  private readonly denoiser: NeuralNetworkGPU<DecodedData, DecodedData>;

  constructor(options?: Partial<IAEOptions>) {
    // Create default options for the autoencoder.
    options ??= {};

    // Create default options for the autoencoder's denoiser subnet.
    const denoiserOptions: Partial<INeuralNetworkGPUOptions> = {};

    // Inherit the binary threshold of the parent autoencoder.
    denoiserOptions.binaryThresh = options.binaryThresh;
    // Inherit the hidden layers of the parent autoencoder.
    denoiserOptions.hiddenLayers = options.hiddenLayers;

    // Define the denoiser subnet's input and output sizes.
    if (options.decodedSize)
      denoiserOptions.inputSize = denoiserOptions.outputSize =
        options.decodedSize;

    // Create the denoiser subnet of the autoencoder.
    this.denoiser = new NeuralNetworkGPU<DecodedData, DecodedData>(options);
  }

  /**
   * Denoise input data, removing any anomalies from the data.
   * @param {DecodedData} input
   * @returns {DecodedData}
   */
  denoise(input: DecodedData): DecodedData {
    // Run the input through the generic denoiser.
    // This isn't the best denoiser implementation, but it's efficient.
    // Efficiency is important here because training should focus on
    // optimizing for feature extraction as quickly as possible rather than
    // denoising and anomaly detection; there are other specialized topologies
    // better suited for these tasks anyways, many of which can be implemented
    // by using an autoencoder.
    return this.denoiser.run(input);
  }

  /**
   * Decode `EncodedData` into an approximation of its original form.
   *
   * @param {EncodedData} input
   * @returns {DecodedData}
   */
  decode(input: EncodedData): DecodedData {
    // If the decoder has not been trained yet, throw an error.
    if (!this.decoder) throw new UntrainedNeuralNetworkError(this);

    // Decode the encoded input.
    return this.decoder.run(input);
  }

  /**
   * Encode data to extract features, reduce dimensionality, etc.
   *
   * @param {DecodedData} input
   * @returns {EncodedData}
   */
  encode(input: DecodedData): EncodedData {
    // If the decoder has not been trained yet, throw an error.
    if (!this.denoiser) throw new UntrainedNeuralNetworkError(this);

    // Process the input.
    this.denoiser.run(input);

    // Get the auto-encoded input.
    let encodedInput: TextureArrayOutput = this
      .encodedLayer as TextureArrayOutput;

    // If the encoded input is a `Texture`, convert it into an `Array`.
    if (encodedInput instanceof Texture) encodedInput = encodedInput.toArray();
    else encodedInput = encodedInput.slice(0);

    // Return the encoded input.
    return encodedInput as EncodedData;
  }

  /**
   * Test whether or not a data sample likely contains anomalies.
   * If anomalies are likely present in the sample, returns `true`.
   * Otherwise, returns `false`.
   *
   * @param {DecodedData} input
   * @returns {boolean}
   */
  likelyIncludesAnomalies(input: DecodedData, anomalyThreshold = 0.2): boolean {
    // Create the anomaly vector.
    const anomalies: number[] = [];

    // Attempt to denoise the input.
    const denoised = this.denoise(input);

    // Calculate the anomaly vector.
    for (let i = 0; i < (input.length ?? 0); i++) {
      anomalies[i] = Math.abs(
        (input as number[])[i] - (denoised as number[])[i]
      );
    }

    // Calculate the sum of all anomalies within the vector.
    const sum = anomalies.reduce(
      (previousValue, value) => previousValue + value
    );

    // Calculate the mean anomaly.
    const mean = sum / (input as number[]).length;

    // Return whether or not the mean anomaly rate is greater than the anomaly threshold.
    return mean > anomalyThreshold;
  }

  /**
   * Train the auto encoder.
   *
   * @param {DecodedData[]} data
   * @param {Partial<INeuralNetworkTrainOptions>} options
   * @returns {INeuralNetworkState}
   */
  train(
    data: DecodedData[],
    options?: Partial<INeuralNetworkTrainOptions>
  ): INeuralNetworkState {
    const preprocessedData: Array<INeuralNetworkDatum<
      Partial<DecodedData>,
      Partial<DecodedData>
    >> = [];

    for (const datum of data) {
      preprocessedData.push({ input: datum, output: datum });
    }

    const results = this.denoiser.train(preprocessedData, options);

    this.decoder = this.createDecoder();

    return results;
  }

  /**
   * Create a new decoder from the trained denoiser.
   *
   * @returns {NeuralNetworkGPU<EncodedData, DecodedData>}
   */
  private createDecoder() {
    const json = this.denoiser.toJSON();

    const layers: IJSONLayer[] = [];
    const sizes: number[] = [];

    for (let i = this.encodedLayerIndex; i < this.denoiser.sizes.length; i++) {
      layers.push(json.layers[i]);
      sizes.push(json.sizes[i]);
    }

    json.layers = layers;
    json.sizes = sizes;

    json.options.inputSize = json.sizes[0];

    const decoder = new NeuralNetworkGPU().fromJSON(json);

    return (decoder as unknown) as NeuralNetworkGPU<EncodedData, DecodedData>;
  }

  /**
   * Get the layer containing the encoded representation.
   */
  private get encodedLayer(): KernelOutput {
    return this.denoiser.outputs[this.encodedLayerIndex];
  }

  /**
   * Get the offset of the encoded layer.
   */
  private get encodedLayerIndex(): number {
    return Math.round(this.denoiser.outputs.length * 0.5) - 1;
  }
}

export default AE;


================================================
FILE: src/cross-validate.test.ts
================================================
import CrossValidate from './cross-validate';
import {
  INeuralNetworkOptions,
  INeuralNetworkTrainOptions,
  NeuralNetwork,
} from './neural-network';
import { LSTMTimeStep } from './recurrent/lstm-time-step';

describe('CrossValidate', () => {
  describe('.train()', () => {
    class FakeNN extends NeuralNetwork<number[], number[]> {
      constructor(
        options: Partial<
          INeuralNetworkOptions & INeuralNetworkTrainOptions
        > = {}
      ) {
        super(options);
        this.options.hiddenLayers = [1, 2, 3];
      }

      train(data: Array<{ input: number[]; output: number[] }>) {
        this.prepTraining(data, this.trainOpts);
        return {
          iterations: 10,
          error: 0.05,
        };
      }
    }
    it('throws exception when training set is too small', () => {
      const xorTrainingData = [{ input: [0, 1], output: [1] }];
      const net = new CrossValidate(() => new FakeNN());
      expect(() => {
        net.train(xorTrainingData);
      }).toThrow('Training set size is too small for 1 k folds of 4');
    });
    it('handles successful training', () => {
      class SpyFakeNN extends FakeNN {
        setActivation() {
          this.runInput = (inputs: Float32Array): Float32Array => {
            if (inputs[0] === 0 && inputs[1] === 1)
              return Float32Array.from([1]);
            if (inputs[0] === 0 && inputs[1] === 0)
              return Float32Array.from([0]);
            if (inputs[0] === 1 && inputs[1] === 1)
              return Float32Array.from([0]);
            if (inputs[0] === 1 && inputs[1] === 0)
              return Float32Array.from([1]);
            throw new Error('unknown input');
          };
        }
      }
      const xorTrainingData = [
        { input: [0, 1], output: [1] },
        { input: [0, 0], output: [0] },
        { input: [1, 1], output: [0] },
        { input: [1, 0], output: [1] },

        { input: [0, 1], output: [1] },
        { input: [0, 0], output: [0] },
        { input: [1, 1], output: [0] },
        { input: [1, 0], output: [1] },
      ];
      const net = new CrossValidate(
        () =>
          new SpyFakeNN({
            inputSize: 1,
            hiddenLayers: [10],
            outputSize: 1,
          })
      );
      net.shuffleArray = (input) => input;
      const result = net.train(xorTrainingData);
      if (!CrossValidate.isBinaryResults(result)) {
        fail('expected binary stats but did not find binary stats');
      }
      expect(result.avgs.iterations).toBe(10);
      expect(result.avgs.error).toBe(0.05);
      expect(result.avgs.testTime >= 0).toBeTruthy();
      expect(result.avgs.trainTime >= 0).toBeTruthy();
      expect(result.stats.total).toBe(8);
      expect(result.stats.truePos).toBe(4);
      expect(result.stats.trueNeg).toBe(4);
      expect(result.stats.falsePos).toBe(0);
      expect(result.stats.falseNeg).toBe(0);
      expect(result.stats.precision).toBe(1);
      expect(result.stats.accuracy).toBe(1);
      expect(result.stats.testSize).toBe(2);
      expect(result.stats.trainSize).toBe(6);
      expect(result.sets.length).toBe(4);
      for (let i = 0; i < result.sets.length; i++) {
        const set = result.sets[0];
        expect(set.accuracy).toBe(1);
        expect(set.error).toBe(0.05);
        expect(set.truePos >= 1 || set.trueNeg >= 1).toBeTruthy();
        expect(set.falseNeg).toBe(0);
        expect(set.falsePos).toBe(0);
        expect(set.precision).toBe(1);
        expect(set.recall).toBe(1);
        expect(set.testTime >= 0).toBeTruthy();
        expect(set.trainTime >= 0).toBeTruthy();
        expect(set.total).toBe(2);
        expect(set.network).not.toBeFalsy();
        expect(set.misclasses).toEqual([]);
      }
    });
    it('handles unsuccessful training', () => {
      class SpyFakeNN extends FakeNN {
        setActivation() {
          this.runInput = (inputs: Float32Array): Float32Array => {
            if (inputs[0] === 0 && inputs[1] === 1)
              return Float32Array.from([0]);
            if (inputs[0] === 0 && inputs[1] === 0)
              return Float32Array.from([1]);
            if (inputs[0] === 1 && inputs[1] === 1)
              return Float32Array.from([1]);
            if (inputs[0] === 1 && inputs[1] === 0)
              return Float32Array.from([0]);
            throw new Error('unknown input');
          };
        }
      }
      const xorTrainingData = [
        { input: [0, 1], output: [1] },
        { input: [0, 0], output: [0] },
        { input: [1, 1], output: [0] },
        { input: [1, 0], output: [1] },

        { input: [0, 1], output: [1] },
        { input: [0, 0], output: [0] },
        { input: [1, 1], output: [0] },
        { input: [1, 0], output: [1] },
      ];
      const net = new CrossValidate(() => new SpyFakeNN());
      net.shuffleArray = (input) => input;
      const result = net.train(xorTrainingData);
      if (!CrossValidate.isBinaryResults(result)) {
        fail('expected binary stats but did not find binary stats');
      }
      expect(result.avgs.iterations).toBe(10);
      expect(result.avgs.error).toBe(0.05);
      expect(result.avgs.testTime >= 0).toBeTruthy();
      expect(result.avgs.trainTime >= 0).toBeTruthy();
      expect(result.stats.total).toBe(8);

      expect(result.stats.truePos).toBe(0);
      expect(result.stats.trueNeg).toBe(0);
      expect(result.stats.falsePos).toBe(4);
      expect(result.stats.falseNeg).toBe(4);
      expect(result.stats.precision).toBe(0);
      expect(result.stats.accuracy).toBe(0);
      expect(result.stats.testSize).toBe(2);
      expect(result.stats.trainSize).toBe(6);

      expect(result.sets.length).toBe(4);
      for (let i = 0; i < result.sets.length; i++) {
        const set = result.sets[0];
        expect(set.accuracy).toBe(0);
        expect(set.error).toBe(0.05);
        expect(set.truePos).toBe(0);
        expect(set.trueNeg).toBe(0);
        expect(set.falseNeg >= 1 || set.falsePos >= 1).toBeTruthy();
        expect(set.precision).toBe(0);
        expect(set.recall).toBe(0);
        expect(set.testTime >= 0).toBeTruthy();
        expect(set.trainTime >= 0).toBeTruthy();
        expect(set.total).toBe(2);
        expect(set.network).not.toBeFalsy();
        interface T {
          misclasses: [
            {
              input: number[];
              output: number[];
              actual: number;
              expected: number;
            }
          ];
        }
        const misclasses = ((set as unknown) as T).misclasses;
        expect(misclasses.length > 0).toBeTruthy();
        expect(misclasses[0].hasOwnProperty('input')).toBeTruthy();
        expect(misclasses[0].input.length).toBeTruthy();
        expect(
          xorTrainingData.filter((v) => v.input === misclasses[0].input)
        ).toBeTruthy();
        expect(
          xorTrainingData.filter((v) => v.output === misclasses[0].output)
        ).toBeTruthy();
        expect(
          misclasses[0].actual === 0 || misclasses[0].actual === 1
        ).toBeTruthy();
        expect(
          misclasses[0].expected === 0 || misclasses[0].expected === 1
        ).toBeTruthy();
      }
    });
  });
  describe('.toJSON()', () => {
    it('returns from this.json', () => {
      const cv = new CrossValidate(() => new NeuralNetwork());
      const json = cv.json;
      expect(cv.toJSON()?.avgs?.error).toBe(0);
      expect(cv.toJSON()).toBe(json);
    });
  });
  describe('.fromJSON()', () => {
    class FakeNN extends NeuralNetwork<number[], number[]> {}
    it("creates a new instance of constructor from argument's sets.error", () => {
      const cv = new CrossValidate(() => new FakeNN(options));
      const options = { inputSize: 1, hiddenLayers: [10], outputSize: 1 };
      const details = {
        trainTime: 0,
        testTime: 0,
        total: 0,
        iterations: 0,
        misclasses: [0],
        learningRate: 0,
        hiddenLayers: [0],
      };
      const bestNetwork = new FakeNN(options);
      bestNetwork.initialize();
      const worstNetwork = new FakeNN(options);
      worstNetwork.initialize();
      const midNetwork = new FakeNN(options);
      midNetwork.initialize();

      const worstNetworkJSON = worstNetwork.toJSON();
      const midNetworkJSON = midNetwork.toJSON();
      const bestNetworkJSON = bestNetwork.toJSON();
      const net = cv.fromJSON({
        avgs: {
          trainTime: 1,
          testTime: 2,
          iterations: 3,
          error: 4,
        },
        stats: {
          total: 5,
          testSize: 6,
          trainSize: 7,
        },
        sets: [
          {
            error: 10,
            network: worstNetworkJSON,
            ...details,
          },
          {
            error: 5,
            network: midNetworkJSON,
            ...details,
          },
          {
            error: 1,
            network: bestNetworkJSON,
            ...details,
          },
        ],
      });

      expect(net.toJSON()).toEqual(bestNetwork.toJSON());
    });
  });
  describe('.toNeuralNetwork()', () => {
    class FakeNN extends NeuralNetwork<number[], number[]> {}
    it('creates a new instance of constructor from top .json sets.error', () => {
      const cv = new CrossValidate(() => new FakeNN());
      const details = {
        trainTime: 0,
        testTime: 0,
        total: 0,
        iterations: 0,
        misclasses: [0],
        learningRate: 0,
        hiddenLayers: [0],
      };
      const options = {
        inputSize: 10,
        hiddenLayers: [10],
        outputSize: 7,
      };
      const bestNet = new FakeNN(options);
      bestNet.initialize();
      const worstNet = new FakeNN(options);
      worstNet.initialize();
      const midNet = new FakeNN(options);
      midNet.initialize();
      cv.json = {
        sets: [
          { error: 10, network: worstNet.toJSON(), ...details },
          { error: 5, network: midNet.toJSON(), ...details },
          { error: 1, network: bestNet.toJSON(), ...details },
        ],
        avgs: { trainTime: 0, testTime: 0, iterations: 0, error: 0 },
        stats: {
          total: 0,
          testSize: 0,
          trainSize: 0,
        },
      };
      const net = cv.toNeuralNetwork();
      expect(net.toJSON()).toEqual(bestNet.toJSON());
    });
  });
  describe('NeuralNetwork compatibility', () => {
    it('handles simple xor example', () => {
      const xorTrainingData = [
        { input: [0, 1], output: [1] },
        { input: [0, 0], output: [0] },
        { input: [1, 1], output: [0] },
        { input: [1, 0], output: [1] },

        { input: [0, 1], output: [1] },
        { input: [0, 0], output: [0] },
        { input: [1, 1], output: [0] },
        { input: [1, 0], output: [1] },
      ];
      const net = new CrossValidate(
        () => new NeuralNetwork<number[], number[]>()
      );
      const result = net.train(xorTrainingData);
      expect(result.avgs.error >= 0).toBeTruthy();
      expect(result.avgs.iterations >= 0).toBeTruthy();
      expect(result.avgs.testTime >= 0).toBeTruthy();
      expect(result.avgs.trainTime >= 0).toBeTruthy();
      expect(result.stats.testSize >= 0).toBeTruthy();
      expect(result.stats.trainSize >= 0).toBeTruthy();
      expect(result.stats.total >= 0).toBeTruthy();
    });
  });

  describe('RNNTimeStep compatibility', () => {
    it('can average error for array,array, counting forwards and backwards', () => {
      const trainingData = [
        [0.1, 0.2, 0.3, 0.4, 0.5],
        [0.2, 0.3, 0.4, 0.5, 0.6],
        [0.3, 0.4, 0.5, 0.6, 0.7],
        [0.4, 0.5, 0.6, 0.7, 0.8],
        [0.5, 0.6, 0.7, 0.8, 0.9],

        [0.5, 0.4, 0.3, 0.2, 0.1],
        [0.6, 0.5, 0.4, 0.3, 0.2],
        [0.7, 0.6, 0.5, 0.4, 0.3],
        [0.8, 0.7, 0.6, 0.5, 0.4],
        [0.9, 0.8, 0.7, 0.6, 0.5],
      ];

      const cv = new CrossValidate(
        () =>
          new LSTMTimeStep({
            inputSize: 1,
            hiddenLayers: [10],
            outputSize: 1,
          })
      );
      const result = cv.train(trainingData, { iterations: 10 });
      expect(!isNaN(result.avgs.error)).toBeTruthy();
    });
  });
});


================================================
FILE: src/cross-validate.ts
================================================
import {
  INeuralNetworkBinaryTestResult,
  INeuralNetworkState,
  INeuralNetworkTestResult,
} from './neural-network-types';

export type InitClassifier<
  TrainOptsType,
  JsonType,
  DatumType
> = () => IClassifier<TrainOptsType, JsonType, DatumType>;

export interface IClassifier<TrainOptsType, JsonType, DatumType> {
  trainOpts: TrainOptsType;
  toJSON: () => JsonType;
  fromJSON: (json: JsonType) => this;
  train: (
    data: DatumType[],
    options?: Partial<TrainOptsType>
  ) => INeuralNetworkState;
  test: (
    data: DatumType[]
  ) => INeuralNetworkTestResult | INeuralNetworkBinaryTestResult;
  initialize: () => void;
}

export type ICrossValidateJSON<JsonType> =
  | ICrossValidateStats<JsonType>
  | ICrossValidateBinaryStats<JsonType>;

export interface ICrossValidateStatsAverages {
  trainTime: number;
  testTime: number;
  iterations: number;
  error: number;
}

export interface ICrossValidateStats<JsonType> {
  avgs: ICrossValidateStatsAverages;
  stats: ICrossValidateStatsResultStats;
  sets: Array<ICrossValidationTestPartitionResults<JsonType>>;
}

export interface ICrossValidateBinaryStats<NetworkType> {
  avgs: ICrossValidateStatsAverages;
  stats: ICrossValidateStatsResultBinaryStats;
  sets: Array<ICrossValidationTestPartitionBinaryResults<NetworkType>>;
}

export interface ICrossValidateStatsResultStats {
  total: number;
  testSize: number;
  trainSize: number;
}

export interface ICrossValidateStatsResultBinaryStats
  extends ICrossValidateStatsResultStats {
  total: number;
  truePos: number;
  trueNeg: number;
  falsePos: number;
  falseNeg: number;
  precision: number;
  recall: number;
  accuracy: number;
}

export interface ICrossValidationTestPartitionResults<JsonType>
  extends INeuralNetworkTestResult {
  trainTime: number;
  testTime: number;
  iterations: number;
  network: JsonType;
  total: number;
}

export type ICrossValidationTestPartitionBinaryResults<
  JsonType
> = INeuralNetworkBinaryTestResult &
  ICrossValidationTestPartitionResults<JsonType>;

export default class CrossValidate<
  InitClassifierType extends InitClassifier<
    ReturnType<InitClassifierType>['trainOpts'],
    ReturnType<ReturnType<InitClassifierType>['toJSON']>,
    Parameters<ReturnType<InitClassifierType>['train']>[0][0]
  >
> {
  initClassifier: InitClassifierType;
  json: ICrossValidateJSON<
    ReturnType<ReturnType<InitClassifierType>['toJSON']>
  > = {
    avgs: {
      error: 0,
      iterations: 0,
      testTime: 0,
      trainTime: 0,
    },
    stats: {
      total: 0,
      testSize: 0,
      trainSize: 0,
    },
    sets: [],
  };

  constructor(initClassifier: InitClassifierType) {
    this.initClassifier = initClassifier;
  }

  testPartition(
    trainOpts: Parameters<ReturnType<InitClassifierType>['train']>[1],
    trainSet: Parameters<ReturnType<InitClassifierType>['train']>[0],
    testSet: Parameters<ReturnType<InitClassifierType>['train']>[0]
  ):
    | ICrossValidationTestPartitionResults<
        ReturnType<ReturnType<InitClassifierType>['toJSON']>
      >
    | ICrossValidationTestPartitionBinaryResults<
        ReturnType<ReturnType<InitClassifierType>['toJSON']>
      > {
    const classifier = this.initClassifier();
    const beginTrain = Date.now();
    const trainingStats = classifier.train(trainSet, trainOpts);
    const beginTest = Date.now();
    const testStats:
      | INeuralNetworkTestResult
      | INeuralNetworkBinaryTestResult = classifier.test(testSet);
    const endTest = Date.now();
    return {
      ...testStats,
      trainTime: beginTest - beginTrain,
      testTime: endTest - beginTest,
      iterations: trainingStats.iterations,
      error: trainingStats.error,
      total: testStats.total,
      network: (classifier as {
        toJSON: () => ReturnType<ReturnType<InitClassifierType>['toJSON']>;
      }).toJSON(),
    };
  }

  /**
   * Randomize array element order in-place.
   * Using Durstenfeld shuffle algorithm.
   * source: http://stackoverflow.com/a/12646864/1324039
   */
  shuffleArray<K>(array: K[]): K[] {
    for (let i = array.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      const temp = array[i];
      array[i] = array[j];
      array[j] = temp;
    }
    return array;
  }

  static isBinaryStats = (
    stats: ICrossValidateStatsResultStats | ICrossValidateStatsResultBinaryStats
  ): stats is ICrossValidateStatsResultBinaryStats => {
    return (
      (stats as ICrossValidateStatsResultBinaryStats).accuracy !== undefined
    );
  };

  static isBinaryResults = <JsonType>(
    stats: ICrossValidateStats<JsonType> | ICrossValidateBinaryStats<JsonType>
  ): stats is ICrossValidateBinaryStats<JsonType> =>
    (stats as ICrossValidateBinaryStats<JsonType>).stats.accuracy !== undefined;

  static isBinaryPartitionResults = <JsonType>(
    stats:
      | ICrossValidationTestPartitionResults<JsonType>
      | ICrossValidationTestPartitionBinaryResults<JsonType>
  ): stats is ICrossValidationTestPartitionBinaryResults<JsonType> =>
    (stats as ICrossValidationTestPartitionBinaryResults<JsonType>).accuracy !==
    undefined;

  train(
    data: Array<Parameters<ReturnType<InitClassifierType>['train']>[0][0]>,
    trainOpts: Partial<
      Parameters<ReturnType<InitClassifierType>['train']>[1]
    > = {},
    k = 4
  ): ICrossValidateStats<ReturnType<InitClassifierType>['toJSON']> {
    if (data.length < k) {
      throw new Error(
        `Training set size is too small for ${data.length} k folds of ${k}`
      );
    }
    this.shuffleArray<unknown>(data);
    const size = data.length / k;

    const avgs: ICrossValidateStatsAverages = {
      trainTime: 0,
      testTime: 0,
      iterations: 0,
      error: 0,
    };

    const stats:
      | ICrossValidateStatsResultStats
      | ICrossValidateStatsResultBinaryStats = {
      total: 0,
      testSize: 0,
      trainSize: 0,
    };

    const binaryStats: ICrossValidateStatsResultBinaryStats = {
      total: 0,
      testSize: 0,
      trainSize: 0,
      truePos: 0,
      trueNeg: 0,
      falsePos: 0,
      falseNeg: 0,
      precision: 0,
      recall: 0,
      accuracy: 0,
    };

    const results = [];
    let isBinary = null;

    for (let i = 0; i < k; i++) {
      const dclone = data.slice(0);
      const testSet = dclone.splice(i * size, size);
      const trainSet = dclone;
      const result = this.testPartition(trainOpts, trainSet, testSet);

      if (isBinary === null) {
        isBinary =
          result.hasOwnProperty('falseNeg') &&
          result.hasOwnProperty('falsePos') &&
          result.hasOwnProperty('trueNeg') &&
          result.hasOwnProperty('truePos');
        if (isBinary) {
          Object.assign(stats, binaryStats);
        }
      }

      avgs.iterations += result.iterations;
      avgs.testTime += result.testTime;
      avgs.trainTime += result.trainTime;
      avgs.error += result.error;
      stats.total += result.total;
      if (
        CrossValidate.isBinaryStats(stats) &&
        CrossValidate.isBinaryPartitionResults(result)
      ) {
        stats.accuracy += result.accuracy;
        stats.falseNeg += result.falseNeg;
        stats.falsePos += result.falsePos;
        stats.precision += result.precision;
        stats.recall += result.recall;
        stats.trueNeg += result.trueNeg;
        stats.truePos += result.truePos;
      }

      results.push(result);
    }
    avgs.error /= k;
    avgs.iterations /= k;
    avgs.testTime /= k;
    avgs.trainTime /= k;

    if (CrossValidate.isBinaryStats(stats)) {
      stats.precision = stats.truePos / (stats.truePos + stats.falsePos);
      stats.recall = stats.truePos / (stats.truePos + stats.falseNeg);
      stats.accuracy = (stats.trueNeg + stats.truePos) / stats.total;
    }

    stats.testSize = size;
    stats.trainSize = data.length - size;

    this.json = {
      avgs: avgs,
      stats: stats,
      sets: results,
    };
    return this.json as ICrossValidateStats<
      ReturnType<InitClassifierType>['toJSON']
    >;
  }

  toNeuralNetwork(): ReturnType<InitClassifierType> {
    return this.fromJSON(this.json);
  }

  toJSON(): ICrossValidateJSON<
    ReturnType<ReturnType<InitClassifierType>['toJSON']>
  > | null {
    return this.json;
  }

  fromJSON(
    crossValidateJson: ICrossValidateJSON<
      ReturnType<ReturnType<InitClassifierType>['toJSON']>
    >
  ): ReturnType<InitClassifierType> {
    const winningJSON:
      | ICrossValidationTestPartitionResults<
          ReturnType<ReturnType<InitClassifierType>['toJSON']>
        >
      | ICrossValidationTestPartitionBinaryResults<
          ReturnType<ReturnType<InitClassifierType>['toJSON']>
        > = (crossValidateJson as ICrossValidateStats<
      ReturnType<ReturnType<InitClassifierType>['toJSON']>
    >).sets.reduce((prev, cur) => (prev.error < cur.error ? prev : cur));
    return (this.initClassifier() as ReturnType<InitClassifierType>).fromJSON(
      winningJSON.network
    );
  }
}


================================================
FILE: src/errors/untrained-neural-network-error.ts
================================================
export class UntrainedNeuralNetworkError<
  T extends { constructor: { name: string } }
> extends Error {
  constructor(neuralNetwork: T) {
    super(
      `Cannot run a ${neuralNetwork.constructor.name} before it is trained.`
    );
  }
}


================================================
FILE: src/estimator/mean-squared-error.ts
================================================
import { IKernelRunShortcut, IKernelFunctionThis } from 'gpu.js';
import { makeKernel } from '../utilities/kernel';

interface mse2dThis extends IKernelFunctionThis {
  constants: { height: number; width: number; length: number };
}

/**
 * 2D Mean Squared Error
 */
export function mse2d(
  this: mse2dThis,
  errors: Array<[number, number]>
): number {
  let sum = 0;
  for (let y = 0; y < this.constants.height; y++) {
    for (let x = 0; x < this.constants.width; x++) {
      sum += errors[y][x] ** 2;
    }
  }
  return sum / this.constants.length;
}

export class MeanSquaredError {
  /** Calculate the mean squared error given an array of errors */
  calculate: IKernelRunShortcut;
  /** Returns the sum of absolute values of previuous error and previous layer errors */
  addAbsolute: IKernelRunShortcut;
  /** Adds two erros */
  add: IKernelRunShortcut;
  /** Returns the ratio of sum of errors and length (ie the average) */
  divide: IKernelRunShortcut;

  constructor({ width, height }: { width: number; height: number }) {
    this.calculate = makeKernel(mse2d, {
      output: [1],
      constants: {
        width,
        height,
        length: width * height,
      },
      immutable: true,
    });

    this.addAbsolute = makeKernel(
      function (prevError: number[], prevLayerErrors: number[][]) {
        return prevError[0] + Math.abs(prevLayerErrors[0][0]);
      },
      {
        output: [1],
        immutable: true,
      }
    );

    this.add = makeKernel(
      function (value1: number[], value2: number[]) {
        return value1[0] + value2[0];
      },
      {
        output: [1],
        immutable: true,
      }
    );

    this.divide = makeKernel(
      function (length: number, mseSum: number[]) {
        const value = mseSum[0];
        if (value > 0) {
          return value / length;
        }
        return 0;
      },
      {
        output: [1],
        immutable: true,
      }
    );
  }
}


================================================
FILE: src/feed-forward.end-to-end.test.ts
================================================
import { GPU } from 'gpu.js';
import { NeuralNetwork } from './neural-network';
import { FeedForward } from './feed-forward';
import {
  input,
  output,
  target,
  Target,
  Sigmoid,
  arthurFeedForward,
  ILayer,
  ILayerSettings,
  feedForward as feedForwardLayer,
} from './layer';

import { momentumRootMeanSquaredPropagation } from './praxis';
import { zeros2D } from './utilities/zeros-2d';
import { setup, teardown } from './utilities/kernel';
import { mockPraxis, xorTrainingData } from './test-utils';
import { IPraxis } from './praxis/base-praxis';

/* eslint-disable no-multi-assign */

describe('FeedForward Class: End to End', () => {
  beforeEach(() => {
    setup(
      new GPU({
        mode: 'cpu',
      })
    );
  });
  afterEach(() => {
    teardown();
  });
  describe('when configured like NeuralNetwork', () => {
    function setupTwinXORNetworks(useDecimals: boolean) {
      const standardNet = new NeuralNetwork();
      const ffNet = new FeedForward({
        inputLayer: () => input({ height: 2, id: 'input' }),
        hiddenLayers: [
          (inputLayer) => arthurFeedForward({ height: 3 }, inputLayer),
          (inputLayer) => arthurFeedForward({ height: 1 }, inputLayer),
        ],
        outputLayer: (inputLayer) =>
          target({ height: 1, id: 'output' }, inputLayer),
      });

      ffNet.initialize();

      standardNet.train([{ input: [1, 1], output: [1] }], {
        iterations: 1,
      });

      // set both nets exactly the same, then train them once, and compare
      const ffNetLayers = ffNet.layers as ILayer[];
      const biasLayers = ffNetLayers.filter((l) => l.id === 'biases');
      const weightLayers = ffNetLayers.filter((l) => l.id === 'weights');
      const sigmoidLayers = ffNetLayers.filter((l) => l instanceof Sigmoid);
      const targetLayer = ffNetLayers[ffNetLayers.length - 1];

      // Use whole numbers to better test accuracy
      // set biases
      const standardNetBiases = standardNet.biases;
      const biasLayers0Weights = biasLayers[0].weights as number[][];
      expect(standardNetBiases[1].length).toBe(3);
      standardNetBiases[1][0] = biasLayers0Weights[0][0] = useDecimals
        ? 0.5
        : 5;
      standardNetBiases[1][1] = biasLayers0Weights[1][0] = useDecimals
        ? 0.7
        : 7;
      standardNetBiases[1][2] = biasLayers0Weights[2][0] = useDecimals
        ? 0.2
        : 2;

      const biasLayers1Weights = biasLayers[1].weights as number[][];
      expect(standardNetBiases[2].length).toBe(1);
      standardNetBiases[2][0] = biasLayers1Weights[0][0] = useDecimals
        ? 0.12
        : 12;

      // set weights
      const standardNetWeights = standardNet.weights;
      const weightLayers0Weights = weightLayers[0].weights as number[][];
      expect(standardNetWeights[1].length).toBe(3);
      expect(standardNetWeights[1][0].length).toBe(2);
      standardNetWeights[1][0][0] = weightLayers0Weights[0][0] = useDecimals
        ? 0.5
        : 5;
      standardNetWeights[1][0][1] = weightLayers0Weights[0][1] = useDecimals
        ? 0.1
        : 10;
      expect(standardNetWeights[1][1].length).toBe(2);
      standardNetWeights[1][1][0] = weightLayers0Weights[1][0] = useDecimals
        ? 0.3
        : 3;
      standardNetWeights[1][1][1] = weightLayers0Weights[1][1] = useDecimals
        ? 0.1
        : 1;
      expect(standardNetWeights[1][2].length).toBe(2);
      standardNetWeights[1][2][0] = weightLayers0Weights[2][0] = useDecimals
        ? 0.8
        : 8;
      standardNetWeights[1][2][1] = weightLayers0Weights[2][1] = useDecimals
        ? 0.4
        : 4;

      const weightLayers1Weights = weightLayers[1].weights as number[][];
      expect(standardNetWeights[2].length).toBe(1);
      expect(standardNetWeights[2][0].length).toBe(3);
      standardNetWeights[2][0][0] = weightLayers1Weights[0][0] = useDecimals
        ? 0.2
        : 2;
      standardNetWeights[2][0][1] = weightLayers1Weights[0][1] = useDecimals
        ? 0.6
        : 6;
      standardNetWeights[2][0][2] = weightLayers1Weights[0][2] = useDecimals
        ? 0.3
        : 3;
      return {
        ffNet,
        standardNet,
        sigmoidLayers,
        targetLayer,
      };
    }
    describe('prediction', () => {
      test('it matches NeuralNetworks.deltas & NeuralNetworks.errors for 2 inputs, 3 hidden neurons, and 1 output', () => {
        const {
          standardNet,
          ffNet,
          sigmoidLayers,
          targetLayer,
        } = setupTwinXORNetworks(true);
        // learning deviates, which we'll test elsewhere, for the time being, just don't learn
        standardNet.adjustWeights = () => {};
        ffNet.adjustWeights = () => {};

        // retrain with these new weights, only ffNet needs reinforce, otherwise, values are lost
        standardNet.train(
          [
            {
              input: new Float32Array([0.9, 0.8]),
              output: new Float32Array([0.5]),
            },
          ],
          {
            iterations: 1,
          }
        );

        ffNet.train(
          [
            {
              input: new Float32Array([0.9, 0.8]),
              output: new Float32Array([0.5]),
            },
          ],
          {
            iterations: 1,
          }
        );

        // test only the sigmoid layers and target layers, as that is the final equation location per layer
        // Also, NeuralNetwork uses a negative value, while FeedForward uses a positive one
        const sigmoidLayers0InputLayerDeltas = (sigmoidLayers[0]
          .inputLayer as ILayer).deltas as number[][];
        const standardNetDeltas = standardNet.deltas;
        expect(-sigmoidLayers0InputLayerDeltas[0][0]).not.toEqual(0);
        expect(-sigmoidLayers0InputLayerDeltas[0][0]).toEqual(
          standardNetDeltas[1][0]
        );
        expect(-sigmoidLayers0InputLayerDeltas[1][0]).not.toEqual(0);
        expect(-sigmoidLayers0InputLayerDeltas[1][0]).toBeCloseTo(
          standardNetDeltas[1][1]
        );
        expect(-sigmoidLayers0InputLayerDeltas[2][0]).not.toEqual(0);
        expect(-sigmoidLayers0InputLayerDeltas[2][0]).toEqual(
          standardNetDeltas[1][2]
        );

        const sigmoidLayers1InputLayerDeltas = (sigmoidLayers[1]
          .inputLayer as ILayer).deltas as number[][];
        expect(-sigmoidLayers1InputLayerDeltas[0][0]).not.toEqual(0);
        expect(-sigmoidLayers1InputLayerDeltas[0][0]).toEqual(
          standardNetDeltas[2][0]
        );

        const targetLayerInputLayerDeltas = (targetLayer.inputLayer as ILayer)
          .deltas as number[][];
        const standardNetErrors = standardNet.errors;
        expect(-targetLayerInputLayerDeltas[0][0]).not.toEqual(0);
        expect(-targetLayerInputLayerDeltas[0][0]).toEqual(
          standardNetErrors[2][0]
        );
      });
    });
    describe('comparison', () => {
      test('it matches NeuralNetwork.outputs for 2 inputs, 3 hidden neurons, and 1 output', () => {
        const {
          standardNet,
          ffNet,
          sigmoidLayers,
          targetLayer,
        } = setupTwinXORNetworks(true);
        // learning deviates, which we'll test elsewhere, for the time being, just don't learn
        standardNet.adjustWeights = function () {};
        ffNet.adjustWeights = function () {};

        // retrain with these new weights, only ffNet needs reinforce, otherwise, values are lost
        standardNet.train([{ input: [0.9, 0.8], output: [0.3] }], {
          iterations: 1,
        });

        ffNet.train([{ input: [0.9, 0.8], output: [0.3] }], {
          iterations: 1,
        });

        // test only the sigmoid layers, as that is the final equation location per layer
        const sigmoidLayers0Weights = sigmoidLayers[0].weights as number[][];
        const standardNetOutputs = standardNet.outputs;
        expect(sigmoidLayers0Weights[0][0]).not.toEqual(0);
        expect(sigmoidLayers0Weights[0][0]).toEqual(standardNetOutputs[1][0]);
        expect(sigmoidLayers0Weights[1][0]).not.toEqual(0);
        expect(sigmoidLayers0Weights[1][0]).toEqual(standardNetOutputs[1][1]);
        expect(sigmoidLayers0Weights[2][0]).not.toEqual(0);
        expect(sigmoidLayers0Weights[2][0]).toEqual(standardNetOutputs[1][2]);

        const sigmoidLayers1Weights = sigmoidLayers[1].weights as number[][];
        expect(sigmoidLayers1Weights[0][0]).not.toEqual(0);
        expect(sigmoidLayers1Weights[0][0]).toEqual(standardNetOutputs[2][0]);

        const targetLayerWeights = targetLayer.weights as number[][];
        expect(targetLayerWeights[0][0]).not.toEqual(0);
        expect(targetLayerWeights[0][0]).toEqual(standardNetOutputs[2][0]);
      });
    });
    describe('learn', () => {
      test('is the same value for 2 inputs, 3 hidden neurons, and 1 output', () => {
        const {
          standardNet,
          ffNet,
          sigmoidLayers,
          targetLayer,
        } = setupTwinXORNetworks(true);

        const sigmoidLayers0WeightsBeforeTraining = sigmoidLayers[0]
          .weights as number[][];
        expect(sigmoidLayers0WeightsBeforeTraining[0][0]).toEqual(0);
        expect(sigmoidLayers0WeightsBeforeTraining[1][0]).toEqual(0);
        expect(sigmoidLayers0WeightsBeforeTraining[2][0]).toEqual(0);

        expect(sigmoidLayers0WeightsBeforeTraining[0][0]).toEqual(0);

        // retrain with these new weights, only ffNet needs reinforce, otherwise, values are lost
        standardNet.train([{ input: [0.9, 0.8], output: [0.3] }], {
          iterations: 1,
        });

        ffNet.train([{ input: [0.9, 0.8], output: [0.3] }], {
          iterations: 1,
        });

        // test only the sigmoid layers, as that is the final equation location per layer
        const sigmoidLayers0WeightsAfterTraining = sigmoidLayers[0]
          .weights as number[][];
        const standardNetOutputs = standardNet.outputs;
        expect(sigmoidLayers0WeightsAfterTraining[0][0]).not.toEqual(0);
        expect(sigmoidLayers0WeightsAfterTraining[0][0]).toEqual(
          standardNetOutputs[1][0]
        );
        expect(sigmoidLayers0WeightsAfterTraining[1][0]).not.toEqual(0);
        expect(sigmoidLayers0WeightsAfterTraining[1][0]).toEqual(
          standardNetOutputs[1][1]
        );
        expect(sigmoidLayers0WeightsAfterTraining[2][0]).not.toEqual(0);
        expect(sigmoidLayers0WeightsAfterTraining[2][0]).toEqual(
          standardNetOutputs[1][2]
        );

        const sigmoidLayers1Weights = sigmoidLayers[1].weights as number[][];
        expect(sigmoidLayers1Weights[0][0]).not.toEqual(0);
        expect(sigmoidLayers1Weights[0][0]).toEqual(standardNetOutputs[2][0]);

        const targetLayerWeights = targetLayer.weights as number[][];
        expect(targetLayerWeights[0][0]).not.toEqual(0);
        expect(targetLayerWeights[0][0]).toEqual(standardNetOutputs[2][0]);
      });
    });
  });

  describe('.runInput()', () => {
    test('outputs a number', () => {
      const net = new FeedForward({
        inputLayer: () => input({ width: 1, height: 1 }),
        hiddenLayers: [
          (inputLayer) => feedForwardLayer({ width: 1, height: 1 }, inputLayer),
        ],
        outputLayer: (inputLayer) =>
          output({ width: 1, height: 1 }, inputLayer),
      });

      net.initialize();
      const result = net.runInput([[1]]) as number[][];

      expect(typeof result[0][0] === 'number').toBeTruthy();
    });
  });

  describe('.train()', () => {
    function testOutputsSmaller() {
      const net = new FeedForward({
        inputLayer: () => input({ height: 2 }),
        hiddenLayers: [
          (inputLayer) => feedForwardLayer({ height: 3 }, inputLayer),
          (inputLayer) => feedForwardLayer({ height: 1 }, inputLayer),
        ],
        outputLayer: (inputLayer) => target({ height: 1 }, inputLayer),
      });
      const errors: number[] = [];
      net.train(xorTrainingData, {
        iterations: 10,
        callbackPeriod: 1,
        errorCheckInterval: 1,
        callback: (info) => errors.push(info.error),
      });

      expect(
        errors.reduce((prev, cur) => prev && typeof cur === 'number', true)
      ).toBeTruthy();

      expect(errors[0]).toBeGreaterThan(errors[errors.length - 1]);
    }

    function testCanLearnXOR() {
      // const errors: number[] = [];
      const net = new FeedForward({
        initPraxis: (layer: ILayer): IPraxis => {
          switch (layer.id) {
            case 'biases':
              return momentumRootMeanSquaredPropagation(layer, {
                decayRate: 0.29,
              });
            case 'weights':
              return momentumRootMeanSquaredPropagation(layer, {
                decayRate: 0.29,
              });
            default:
              return mockPraxis(layer);
          }
        },
        inputLayer: () => input({ height: 2 }),
        hiddenLayers: [
          (inputLayer) => feedForwardLayer({ height: 3 }, inputLayer),
          (inputLayer) => feedForwardLayer({ height: 1 }, inputLayer),
        ],
        outputLayer: (inputLayer) => target({ height: 1 }, inputLayer),
      });

      net.train(xorTrainingData, {
        callbackPeriod: 1,
        errorCheckInterval: 200,
        callback: (info) => {
          if (info.iterations % 200 === 0) {
            // errors.push(info.error);
          }
        },
      });

      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const result1 = net.run([0, 0]);
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const result2 = net.run([0, 1]);
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const result3 = net.run([1, 0]);
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const result4 = net.run([1, 1]);

      // TODO: this should be easier than result[0][0] https://github.com/BrainJS/brain.js/issues/439
      // expect(result1[0][0]).toBeLessThan(0.2);
      // expect(result2[0][0]).toBeGreaterThan(0.8);
      // expect(result3[0][0]).toBeGreaterThan(0.8);
      // expect(result4[0][0]).toBeLessThan(0.2);
      // expect(errors[errors.length - 1]).toBeLessThan(0.1);
      // expect(errors.length).toBeLessThan(net.trainOpts.iterations);
    }

    describe('on CPU', () => {
      test('outputs a number that is smaller than when it started', () => {
        testOutputsSmaller();
      });
      test('can learn xor', () => {
        testCanLearnXOR();
      });
    });
    describe('on GPU', () => {
      if (!GPU.isGPUSupported) return;
      beforeEach(() => {
        setup(new GPU({ mode: 'gpu' }));
      });
      afterEach(() => {
        teardown();
      });
      test('outputs a number that is smaller than when it started', () => {
        testOutputsSmaller();
      });
      test('can learn xor', () => {
        testCanLearnXOR();
      });
    });
  });

  describe('.trainAsync()', () => {
    it('can be used to train XOR', async () => {
      const net = new FeedForward({
        inputLayer: () => input({ height: 2 }),
        hiddenLayers: [
          (inputLayer) => feedForwardLayer({ height: 3 }, inputLayer),
          (inputLayer) => feedForwardLayer({ height: 1 }, inputLayer),
        ],
        outputLayer: (inputLayer) => target({ height: 1 }, inputLayer),
      });
      const errors: number[] = [];
      const result = await net.trainAsync(xorTrainingData, {
        iterations: 10,
        callbackPeriod: 1,
        errorCheckInterval: 1,
        callback: (info) => errors.push(info.error),
      });
      expect(result.error).toBeLessThan(1);
      expect(result.iterations).toBe(10);
    });
  });

  describe('._calculateDeltas()', () => {
    test('populates deltas from output to input', () => {
      class SuperOutput extends Target {
        constructor(settings: ILayerSettings, inputLayer: ILayer) {
          super(settings, inputLayer);
          this.deltas = zeros2D(this.width, this.height);
          this.inputLayer = inputLayer;
        }
      }

      const net = new FeedForward({
        inputLayer: () => input({ width: 1, height: 1 }),
        hiddenLayers: [
          (inputLayer) => feedForwardLayer({ width: 1, height: 1 }, inputLayer),
        ],
        outputLayer: (inputLayer) =>
          new SuperOutput({ width: 1, height: 1 }, inputLayer),
      });
      net.initialize();
      const layers: ILayer[] = net.layers as ILayer[];
      layers[0].weights = [[1]];

      layers.forEach((layerLayer) => {
        (layerLayer.deltas as number[][]).forEach((row) => {
          row.forEach((delta) => {
            expect(delta).toBe(0);
          });
        });
      });
      net.runInput([[1]]);
      net._calculateDeltas([[1]]);

      layers.forEach((l) => {
        (l.deltas as number[][]).forEach((row) => {
          row.forEach((delta) => {
            expect(delta === 0).toBeFalsy();
          });
        });
      });
    });
  });
});


================================================
FILE: src/feed-forward.ts
================================================
import { IKernelFunctionThis, KernelOutput, Texture } from 'gpu.js';
import { MeanSquaredError } from './estimator/mean-squared-error';
import { ILayer, ILayerJSON } from './layer';
import { Model } from './layer/types';
import { InputOutputValue, INumberArray, INumberHash, lookup } from './lookup';
import * as praxis from './praxis';
import { IPraxis, IPraxisSettings } from './praxis/base-praxis';
import { flattenLayers } from './utilities/flatten-layers';
import { makeKernel, release } from './utilities/kernel';
import { layerFromJSON } from './utilities/layer-from-json';
import { LookupTable } from './utilities/lookup-table';
import { Thaw } from 'thaw.js';

export interface IFeedForwardTrainingData<
  InputType extends InputOutputValue | KernelOutput = number[] | Float32Array,
  OutputType extends InputOutputValue | KernelOutput = number[] | Float32Array
> {
  input: InputType;
  output: OutputType;
}

export interface IFeedForwardNormalizedTrainingData {
  input: Float32Array;
  output: Float32Array;
}

export interface IFeedForwardGPUTrainingData {
  input: KernelOutput;
  output: KernelOutput;
}

export interface ITrainingStatus {
  iterations: number;
  error: number;
}

export type Log = (status: string) => void;
export type FeedForwardCallback = (status: ITrainingStatus) => void;

export interface IFeedForwardTrainingOptions {
  iterations?: number;
  errorThresh?: number;
  log?: boolean | Log;
  logPeriod?: number;
  learningRate?: number;
  callback?: FeedForwardCallback;
  callbackPeriod?: number;
  errorCheckInterval?: number;
  timeout?: number;
}

export interface IFeedForwardOptions {
  learningRate?: number;
  binaryThresh?: number;
  hiddenLayers?: Array<(inputLayer: ILayer, layerIndex: number) => ILayer>;
  inputLayer?: () => ILayer;
  outputLayer?: (inputLayer: ILayer, index: number) => ILayer;
  praxisOpts?: Partial<IPraxisSettings>;
  initPraxis?: (
    layerTemplate: ILayer,
    settings: Partial<IPraxisSettings>
  ) => IPraxis;
  praxis?: IPraxis;

  // JSON
  layers?: ILayer[];
  inputLayerIndex?: number;
  outputLayerIndex?: number;
  sizes?: number[];
}

export interface IFeedForwardPreppedTrainingData {
  status: ITrainingStatus;
  preparedData: IFeedForwardGPUTrainingData[];
  endTime: number;
}

export const defaults: IFeedForwardOptions = {
  learningRate: 0.3,
  binaryThresh: 0.5,
  initPraxis: (
    layerTemplate: ILayer,
    settings: Partial<IPraxisSettings>
  ): IPraxis =>
    praxis.momentumRootMeanSquaredPropagation(
      layerTemplate,
      layerTemplate.settings.praxisOpts ?? settings
    ),
};

export const trainDefaults: IFeedForwardTrainingOptions = {
  iterations: 20000,
  errorThresh: 0.005,
  log: false,
  logPeriod: 10,
  learningRate: 0.3,
  callbackPeriod: 10,
  errorCheckInterval: 100,
  timeout: Infinity,
};

export interface IFeedForwardJSON {
  type: string;
  sizes: number[];
  layers: ILayerJSON[];
  inputLayerIndex: number;
  outputLayerIndex: number;
}

export class FeedForward<
  InputType extends InputOutputValue | KernelOutput = number[] | Float32Array,
  OutputType extends InputOutputValue | KernelOutput = number[] | Float32Array
> {
  static _validateTrainingOptions(
    options: Partial<IFeedForwardTrainingOptions>
  ): void {
    const {
      iterations,
      errorThresh,
      log,
      logPeriod,
      learningRate,
      callback,
      callbackPeriod,
      timeout,
    } = options;
    interface IValidation {
      [optionName: string]: () => boolean;
    }
    const validations: IValidation = {
      iterations: () => typeof iterations === 'number' && iterations > 0,
      errorThresh: () =>
        typeof errorThresh === 'number' && errorThresh > 0 && errorThresh < 1,
      log: () => typeof log === 'function' || typeof log === 'boolean',
      logPeriod: () => typeof logPeriod === 'number' && logPeriod > 0,
      learningRate: () =>
        typeof learningRate === 'number' &&
        learningRate > 0 &&
        learningRate < 1,
      callback: () => typeof callback === 'function' || callback === null,
      callbackPeriod: () =>
        typeof callbackPeriod === 'number' && callbackPeriod > 0,
      timeout: () => typeof timeout === 'number' && timeout > 0,
    };
    Object.keys(trainDefaults).forEach((key: string): void => {
      if (validations.hasOwnProperty(key) && !validations[key]()) {
        const val = options[key as keyof IFeedForwardTrainingOptions];
        throw new Error(
          `[${key}, ${(
            val ?? 'undefined'
          ).toString()}] is out of normal training range, your network will probably not train.`
        );
      }
    });
  }

  /**
   * if a method is passed in method is used
   * if false passed in nothing is logged
   */
  _setLogMethod(log: Log | undefined | boolean): void {
    if (typeof log === 'function') {
      this.trainOpts.log = log;
    } else if (log) {
      // eslint-disable-next-line
      this.trainOpts.log = console.log;
    } else {
      this.trainOpts.log = false;
    }
  }

  _updateTrainingOptions(opts: Partial<IFeedForwardTrainingOptions>): void {
    this.trainOpts = { ...trainDefaults, ...this.trainOpts, ...opts };
    FeedForward._validateTrainingOptions(this.trainOpts);
    this._setLogMethod(opts.log ?? this.trainOpts.log);
    const { callback, callbackPeriod, errorCheckInterval } = this.trainOpts;
    if (callback && callbackPeriod !== errorCheckInterval) {
      console.warn(
        `options.callbackPeriod with value of ${(
          callbackPeriod ?? 'undefined'
        ).toString()} does not match options.errorCheckInterval with value of ${(
          errorCheckInterval ?? 'undefined'
        ).toString()}, if logging error, it will repeat.  These values may need to match`
      );
    }
  }

  trainOpts: Partial<IFeedForwardTrainingOptions> = {};
  options: IFeedForwardOptions;
  layers: ILayer[] | null = null;
  _inputLayer: ILayer | null = null;
  _hiddenLayers: ILayer[] | null = null;
  _outputLayer: ILayer | null = null;
  _model: ILayer[] | null = null;
  meanSquaredError: MeanSquaredError | null = null;
  inputLookup: INumberHash | null = null;
  inputLookupLength: number | null = null;
  outputLookup: INumberHash | null = null;
  outputLookupLength: number | null = null;
  constructor(options: IFeedForwardOptions = {}) {
    this.options = { ...defaults, ...options };
    this._updateTrainingOptions({
      ...trainDefaults,
      ...options,
    });
  }

  _connectOptionsLayers(): ILayer[] {
    const { inputLayerIndex, outputLayerIndex, layers } = this.options;
    if (!layers) throw new Error('this.options.layers in unexpected state');
    if (typeof inputLayerIndex !== 'number')
      throw new Error('inputLayerIndex not a number');
    if (typeof outputLayerIndex !== 'number')
      throw new Error('inputLayerIndex not a number');
    const inputLayer = layers[inputLayerIndex];
    if (!inputLayer) {
      throw new Error('inputLayer not found in this.options.layers');
    }
    const outputLayer = layers[outputLayerIndex];
    if (!outputLayer) {
      throw new Error('outputLayer not found in this.options.layers');
    }
    this._inputLayer = inputLayer;
    this._hiddenLayers = layers.slice(
      inputLayerIndex,
      outputLayerIndex - inputLayerIndex
    );
    this._outputLayer = outputLayer;
    return layers;
  }

  _connectNewLayers(): ILayer[] {
    const { inputLayer, outputLayer } = this.options;
    if (!inputLayer) throw new Error('inputLayer not defined');
    const layers: ILayer[] = [];
    this._inputLayer = inputLayer();
    const hiddenLayers = this._connectHiddenLayers(this._inputLayer);

    if (!outputLayer) throw new Error('outputLayer not defined');
    this._outputLayer = outputLayer(
      hiddenLayers[hiddenLayers.length - 1],
      hiddenLayers.length
    );
    layers.push(this._inputLayer);
    layers.push(...hiddenLayers);
    layers.push(this._outputLayer);
    return flattenLayers(layers);
  }

  _connectHiddenLayers(previousLayer: ILayer): ILayer[] {
    this._hiddenLayers = [];
    const result: ILayer[] = [];
    const { hiddenLayers } = this.options;

    if (!hiddenLayers) throw new Error('hiddenLayers not defined');

    for (let i = 0; i < hiddenLayers.length; i++) {
      const hiddenLayer = hiddenLayers[i](previousLayer, i);
      result.push(hiddenLayer);
      this._hiddenLayers.push(hiddenLayer);
      previousLayer = hiddenLayer;
    }

    return result;
  }

  initialize(): void {
    this.layers = this.options.layers
      ? this._connectOptionsLayers()
      : this._connectNewLayers();
    this.initializeLayers(this.layers);
    this._model = this.layers.filter((l) => l instanceof Model);
  }

  initializeLayers(layers: ILayer[]): void {
    for (let i = 0; i < layers.length; i++) {
      const layer = layers[i];
      // TODO: optimize for when training or just running
      layer.setupKernels(true);
      if (
        layer instanceof Model &&
        layer.praxis === null &&
        typeof this.options.initPraxis === 'function'
      ) {
        layer.praxis = this.options.initPraxis(
          layer,
          layer.settings.praxisOpts ?? this.options.praxisOpts ?? {}
        );
        layer.praxis.setupKernels();
      }
    }

    const lastLayer = layers[layers.length - 1];
    this.meanSquaredError = new MeanSquaredError({
      width: lastLayer.width,
      height: lastLayer.height,
    });
  }

  run(input: InputType): OutputType {
    let typeSafeInput: INumberArray | KernelOutput;
    if (Array.isArray(input) || (input as Float32Array).buffer) {
      typeSafeInput = input as INumberArray;
    } else {
      if (this.inputLookup) {
        typeSafeInput = lookup.toArray(
          this.inputLookup,
          input as INumberHash,
          this.inputLookupLength as number
        );
      } else {
        throw new Error('input is incompatible with net');
      }
    }

    let output = this.runInput(typeSafeInput as KernelOutput);
    if (output instanceof Texture) {
      output = output.toArray();
    }

    if (this.outputLookup) {
      return lookup.toObject(
        this.outputLookup,
        output as number[]
      ) as OutputType;
    }
    return output as OutputType;
  }

  runInput(input: KernelOutput): KernelOutput {
    if (!this.layers) throw new Error('not initialized');
    this.layers[0].predict(input);
    for (let i = 1; i < this.layers.length; i++) {
      this.layers[i].predict();
    }
    return this.layers[this.layers.length - 1].weights as KernelOutput;
  }

  train(
    data: Array<IFeedForwardTrainingData<InputType, OutputType>>,
    options: Partial<IFeedForwardTrainingOptions> = {}
  ): ITrainingStatus {
    const { preparedData, status, endTime } = this._prepTraining(data, options);
    let continueTicking = true;
    const calculateError = (): number =>
      this._calculateTrainingError(preparedData);
    const trainPatterns = (): void => this._trainPatterns(preparedData);
    while (continueTicking) {
      continueTicking = this._trainingTick(
        status,
        endTime,
        calculateError,
        trainPatterns
      );
    }
    return status;
  }

  async trainAsync(
    data: Array<IFeedForwardTrainingData<InputType, OutputType>>,
    options: Partial<IFeedForwardTrainingOptions> = {}
  ): Promise<ITrainingStatus> {
    const { preparedData, status, endTime } = this._prepTraining(data, options);

    return await new Promise((resolve, reject) => {
      try {
        const calculateError = (): number =>
          this._calculateTrainingError(preparedData);
        const trainPatterns = (): void => this._trainPatterns(preparedData);
        const thawedTrain: Thaw = new Thaw(
          new Array(this.trainOpts.iterations),
          {
            delay: true,
            each: () =>
              this._trainingTick(
                status,
                endTime,
                calculateError,
                trainPatterns
              ) || thawedTrain.stop(),
            done: () => resolve(status),
          }
        );
        thawedTrain.tick();
      } catch (trainError) {
        reject(trainError);
      }
    });
  }

  _trainingTick(
    status: ITrainingStatus,
    endTime: number,
    calculateError: () => number,
    trainPatterns: () => void
  ): boolean {
    const { trainOpts } = this;
    if (
      status.iterations >= (trainOpts.iterations as number) ||
      status.error <= (trainOpts.errorThresh as number) ||
      Date.now() >= endTime
    ) {
      return false;
    }

    if (
      typeof trainOpts.log === 'function' &&
      status.iterations % (trainOpts.logPeriod as number) === 0
    ) {
      status.error = calculateError();
      trainOpts.log(
        `iterations: ${status.iterations}, training error: ${status.error}`
      );
    } else if (
      status.iterations % (trainOpts.errorCheckInterval as number) ===
      0
    ) {
      status.error = calculateError();
    } else {
      trainPatterns();
    }

    if (
      trainOpts.callback &&
      status.iterations % (trainOpts.callbackPeriod as number) === 0
    ) {
      trainOpts.callback(Object.assign(status));
    }

    status.iterations++;
    return true;
  }

  _prepTraining(
    data: Array<IFeedForwardTrainingData<InputType, OutputType>>,
    options: Partial<IFeedForwardTrainingOptions>
  ): IFeedForwardPreppedTrainingData {
    this._updateTrainingOptions(options);

    const formattedData = this.formatData(data);
    const endTime = this.trainOpts.timeout
      ? Date.now() + this.trainOpts.timeout
      : 0;

    const status = {
      error: 1,
      iterations: 0,
    };

    this.verifyIsInitialized();

    return {
      preparedData: this.transferData(formattedData),
      status,
      endTime,
    };
  }

  verifyIsInitialized(): void {
    if (!this._model) {
      this.initialize();
    }
  }

  _calculateTrainingError(preparedData: IFeedForwardGPUTrainingData[]): number {
    let sum: Float32Array | KernelOutput = new Float32Array([0]);
    const meanSquaredError = this.meanSquaredError as MeanSquaredError;
    for (let i = 0; i < preparedData.length; ++i) {
      const prevSum = sum;
      const error = this._trainPattern(
        preparedData[i].input,
        preparedData[i].output,
        true
      ) as number;
      sum = meanSquaredError.add(sum, error);
      release(error);
      release(prevSum);
    }
    const result = meanSquaredError.divide(preparedData.length, sum);
    release(sum);
    if (result instanceof Texture) {
      const resultArray: number[] = result.toArray() as number[];
      release(result);
      return resultArray[0];
    }
    return (result as number[])[0];
  }

  /**
   * @param data
   * @private
   */
  _trainPatterns(data: IFeedForwardGPUTrainingData[]): void {
    for (let i = 0; i < data.length; ++i) {
      this._trainPattern(data[i].input, data[i].output, false);
    }
  }

  _trainPattern(
    input: KernelOutput,
    target: KernelOutput,
    logErrorRate: boolean
  ): KernelOutput | null {
    // forward propagate
    this.runInput(input);

    // back propagate
    this._calculateDeltas(target);
    this.adjustWeights();

    if (logErrorRate) {
      if (!this._outputLayer?.errors) {
        throw new Error('outputLayer.errors not defined');
      }
      return (this.meanSquaredError as MeanSquaredError).calculate(
        this._outputLayer.errors
      );
    }
    return null;
  }

  _calculateDeltas(target: KernelOutput): void {
    const layers = this.layers as ILayer[];
    for (let i = layers.length - 1; i > -1; i--) {
      layers[i].compare(target);
    }
  }

  /**
   *
   */
  adjustWeights(): void {
    const _model = this._model as ILayer[];
    for (let i = 0; i < _model.length; i++) {
      _model[i].learn(this.trainOpts.learningRate as number);
    }
  }

  /**
   *
   * @param data
   * @returns {*}
   */
  formatData(
    data:
      | Array<IFeedForwardTrainingData<InputType, OutputType>>
      | IFeedForwardTrainingData<InputType, OutputType>
  ): IFeedForwardNormalizedTrainingData[] {
    if (!Array.isArray(data)) {
      // turn stream datum into array
      const tmp = [];
      tmp.push(data);
      data = tmp;
    }

    // turn sparse hash input into arrays with 0s as filler
    const inputDatumCheck = data[0].input;
    let formattedData: Array<Partial<IFeedForwardNormalizedTrainingData>>;
    if (
      Array.isArray(data) &&
      !Array.isArray(inputDatumCheck) &&
      !(inputDatumCheck instanceof Float32Array)
    ) {
      if (!this.inputLookup) {
        const lookupTable = new LookupTable(data, 'input');
        this.inputLookup = lookupTable.table;
        this.inputLookupLength = lookupTable.length;
      }
      formattedData = data.map((datumParam): Partial<
        IFeedForwardNormalizedTrainingData
      > => {
        const array = lookup.toArray(
          this.inputLookup as INumberHash,
          datumParam.input as INumberHash,
          this.inputLookupLength as number
        );
        return { input: array };
      }, this);
    } else {
      formattedData = data as typeof formattedData;
    }

    const outputDatumCheck = data[0].output;
    if (
      !Array.isArray(outputDatumCheck) &&
      !(outputDatumCheck instanceof Float32Array)
    ) {
      if (!this.outputLookup) {
        const lookupTable = new LookupTable(data, 'output');
        this.outputLookup = lookupTable.table;
        this.outputLookupLength = lookupTable.length;
      }
      formattedData = data.map(
        (datumParam, index): IFeedForwardNormalizedTrainingData => {
          const array = lookup.toArray(
            this.outputLookup as INumberHash,
            datumParam.output as INumberHash,
            this.inputLookupLength as number
          );
          return {
            input: formattedData[index].input as Float32Array,
            output: array,
          };
        },
        this
      );
    }
    return formattedData as IFeedForwardNormalizedTrainingData[];
  }

  transferData(
    formattedData: IFeedForwardNormalizedTrainingData[]
  ): IFeedForwardGPUTrainingData[] {
    const transferredData = new Array(formattedData.length);
    const transferInput = makeKernel(
      function (value: number[]): number {
        return value[this.thread.x];
      },
      {
        output: [formattedData[0].input.length],
        immutable: true,
      }
    );
    const transferOutput = makeKernel(
      function (this: IKernelFunctionThis, value: number[]): number {
        return value[this.thread.x];
      },
      {
        output: [formattedData[0].output.length],
        immutable: true,
      }
    );

    for (let i = 0; i < formattedData.length; i++) {
      const formattedDatum = formattedData[i];
      transferredData[i] = {
        input: transferInput(formattedDatum.input),
        output: transferOutput(formattedDatum.output),
      };
    }
    return transferredData;
  }

  /**
   *
   * @param data
   * @returns {
   *  {
   *    error: number,
   *    misclasses: Array
   *  }
   * }
   */
  test(): void {
    throw new Error(`${this.constructor.name}-test is not yet implemented`);
  }

  /**
   *
   */
  toJSON(): IFeedForwardJSON {
    if (!this.layers) {
      this.initialize();
    }
    if (
      !this._model ||
      !this.layers ||
      !this._inputLayer ||
      !this._hiddenLayers ||
      !this._outputLayer
    ) {
      throw new Error('network is not initialized');
    }
    const jsonLayers = [];
    for (let i = 0; i < this.layers.length; i++) {
      const layer = this.layers[i];
      const jsonLayer = layer.toJSON();
      if (layer.hasOwnProperty('inputLayer')) {
        jsonLayer.inputLayerIndex = this.layers.indexOf(
          layer.inputLayer as ILayer
        );
      } else if (
        layer.hasOwnProperty('inputLayer1') &&
        layer.hasOwnProperty('inputLayer2')
      ) {
        jsonLayer.inputLayer1Index = this.layers.indexOf(
          layer.inputLayer1 as ILayer
        );
        jsonLayer.inputLayer2Index = this.layers.indexOf(
          layer.inputLayer2 as ILayer
        );
      }
      jsonLayers.push(jsonLayer);
    }

    return {
      type: this.constructor.name,
      sizes:
        this.options.sizes ??
        [this._inputLayer.height]
          .concat(this._hiddenLayers.map((l) => l.height))
          .concat([this._outputLayer.height]),
      outputLayerIndex: this.layers.indexOf(this._outputLayer),
      layers: jsonLayers as ILayerJSON[],
      inputLayerIndex: this.layers.indexOf(this._inputLayer),
    };
  }

  static fromJSON(
    json: IFeedForwardJSON,
    getLayer?: (
      layerJson: ILayerJSON,
      inputLayer1?: ILayer,
      inputLayer2?: ILayer
    ) => ILayer
  ): FeedForward {
    const jsonLayers = json.layers;
    const layers: ILayer[] = [];
    const inputLayer = getLayer
      ? layerFromJSON(jsonLayers[0]) ?? getLayer(jsonLayers[0])
      : layerFromJSON(jsonLayers[0]);

    if (!inputLayer) throw new Error('unable to find layer');

    layers.push(inputLayer);

    for (let i = 1; i < jsonLayers.length; i++) {
      const jsonLayer = jsonLayers[i];
      if (
        typeof jsonLayer.inputLayerIndex === 'undefined' &&
        typeof jsonLayer.inputLayer1Index === 'undefined' &&
        typeof jsonLayer.inputLayer2Index === 'undefined'
      ) {
        const layer = getLayer
          ? layerFromJSON(jsonLayer) ?? getLayer(jsonLayer)
          : layerFromJSON(jsonLayer);
        if (!layer) throw new Error('unable to find layer');
        layers.push(layer);
      } else if (typeof jsonLayer.inputLayerIndex === 'number') {
        const inputLayer = layers[jsonLayer.inputLayerIndex];
        if (!inputLayer) {
          throw new Error('inputLayer1 not found');
        }
        const layer = getLayer
          ? layerFromJSON(jsonLayer, inputLayer) ??
            getLayer(jsonLayer, inputLayer)
          : layerFromJSON(jsonLayer, inputLayer);
        if (!layer) throw new Error('unable to find layer');
        layers.push(layer);
      } else {
        if (typeof jsonLayer.inputLayer1Index !== 'number') {
          throw new Error(
            'Cannot create network from provided JSON. inputLayer1Index not defined.'
          );
        }
        if (typeof jsonLayer.inputLayer2Index !== 'number') {
          throw new Error(
            'Cannot create network from provided JSON. inputLayer2Index not defined.'
          );
        }
        const inputLayer1 = layers[jsonLayer.inputLayer1Index];
        const inputLayer2 = layers[jsonLayer.inputLayer2Index];

        if (inputLayer1 === undefined)
          throw new Error(
            `Cannot create network from provided JSON. layer of index ${jsonLayer.inputLayer1Index} not found.`
          );
        if (inputLayer2 === undefined)
          throw new Error(
            `Cannot create network from provided JSON. layer of index ${jsonLayer.inputLayer2Index} not found.`
          );

        const layer = getLayer
          ? layerFromJSON(jsonLayer, inputLayer1, inputLayer2) ??
            getLayer(jsonLayer, inputLayer1, inputLayer2)
          : layerFromJSON(jsonLayer, inputLayer1, inputLayer2);

        if (!layer) throw new Error('unable to find layer');
        layers.push(layer);
      }
    }

    return new this({ ...json, layers });
  }

  /**
   *
   * @returns {Function}
   */
  toFunction(): void {
    throw new Error(
      `${this.constructor.name}-toFunction is not yet implemented`
    );
  }
}


================================================
FILE: src/feed-forward.unit.test.ts
================================================
import { GPU } from 'gpu.js';
import { setup, teardown } from './utilities/kernel';
import { FeedForward } from './feed-forward';
import {
  Add,
  BaseLayer,
  Convolution,
  convolution,
  feedForward,
  Input,
  input,
  Multiply,
  // Output,
  output,
  Pool,
  pool,
  Random,
  Relu,
  relu,
  Sigmoid,
  SoftMax,
  softMax,
  Target,
  // Zeros,
  layerTypes,
  ILayer,
  ILayerJSON,
  ILayerSettings,
} from './layer';
import { mockLayer, mockPraxis } from './test-utils';
import SpyInstance = jest.SpyInstance;

describe('FeedForward Class: Unit', () => {
  beforeEach(() => {
    setup(
      new GPU({
        mode: 'cpu',
      })
    );
  });
  afterEach(() => {
    teardown();
  });
  describe('.constructor()', () => {
    test('initially does not have any layers', () => {
      expect(new FeedForward().layers).toBeNull();
    });
  });

  describe('layer composition', () => {
    const addValidate = Add.prototype.validate;
    beforeEach(() => {
      Add.prototype.validate = () => {};
    });
    afterEach(() => {
      Add.prototype.validate = addValidate;
    });
    describe('flat', () => {
      test('can setup and traverse entire network as needed', () => {
        const net = new FeedForward({
          inputLayer: () => input({ width: 1 }),
          hiddenLayers: [
            (inputLayer: ILayer) =>
              convolution(
                {
                  filterCount: 8,
                  filterWidth: 5,
                  filterHeight: 5,
                  padding: 2,
                  stride: 1,
                },
                inputLayer
              ),
            (inputLayer: ILayer) => relu(inputLayer),
            (inputLayer: ILayer) =>
              pool(
                {
                  filterHeight: 3,
                  filterWidth: 3,
                  filterCount: 1,
                  padding: 2,
                  stride: 2,
                },
                inputLayer
              ),
            (inputLayer: ILayer) =>
              convolution(
                {
                  padding: 2,
                  stride: 1,
                  filterCount: 16,
                  filterWidth: 5,
                  filterHeight: 5,
                },
                inputLayer
              ),
            (inputLayer: ILayer) => relu(inputLayer),
            (inputLayer: ILayer) =>
              pool(
                {
                  padding: 2,
                  filterWidth: 3,
                  filterHeight: 3,
                  filterCount: 1,
                  stride: 3,
                },
                inputLayer
              ),
            (inputLayer: ILayer) => softMax(inputLayer),
          ],
          outputLayer: (inputLayer: ILayer) =>
            output({ height: 10 }, inputLayer),
        });

        net.initialize();

        const layers = net.layers as ILayer[];
        expect(layers.length).toBe(13);
        expect(layers.map((l: ILayer) => l.constructor)).toEqual([
          Input,
          Convolution,
          Relu,
          Pool,
          Convolution,
          Relu,
          Pool,
          SoftMax,
          Random,
          Multiply,
          Random,
          Add,
          Target,
        ]);
      });

      test('can setup and traverse entire network using layer composed of layers', () => {
        const net = new FeedForward({
          inputLayer: () => input({ height: 1 }),
          hiddenLayers: [
            (inputLayer) => feedForward({ height: 1 }, inputLayer),
          ],
          outputLayer: (inputLayer) => output({ height: 1 }, inputLayer),
        });

        net.initialize();

        const layers = net.layers as ILayer[];
        expect(layers.length).toBe(11);
        expect(layers.map((l) => l.constructor)).toEqual([
          Input,
          Random,
          Multiply,
          Random,
          Add,
          Sigmoid,
          Random,
          Multiply,
          Random,
          Add,
          Target,
        ]);
      });
    });

    describe('functional', () => {
      test('can setup and traverse entire network as needed', () => {
        const net = new FeedForward({
          inputLayer: () => input({ width: 1 }),
          hiddenLayers: [
            (inputParam) =>
              softMax(
                pool(
                  {
                    filterWidth: 3, // TODO: setting height, width should behave same
                    filterHeight: 3,
                    filterCount: 3,
                    padding: 2,
                    stride: 3,
                  },
                  relu(
                    convolution(
                      {
                        padding: 2,
                        stride: 1,
                        filterCount: 16,
                        filterWidth: 5,
                        filterHeight: 5,
                      },
                      pool(
                        {
                          filterWidth: 3,
                          filterHeight: 3,
                          filterCount: 16,
                          padding: 2,
                          stride: 2,
                        },
                        relu(
                          convolution(
                            {
                              filterCount: 8,
                              filterWidth: 5,
                              filterHeight: 5,
                              padding: 2,
                              stride: 1,
                            },
                            inputParam
                          )
                        )
                      )
                    )
                  )
                )
              ),
          ],
          outputLayer: (inputParam) => output({ height: 10 }, inputParam),
        });
        net.initialize();

        const layers = net.layers as ILayer[];
        expect(layers.length).toBe(13);
        expect(layers.map((l) => l.constructor)).toEqual([
          Input,
          Convolution,
          Relu,
          Pool,
          Convolution,
          Relu,
          Pool,
          SoftMax,
          Random,
          Multiply,
          Random,
          Add,
          Target,
        ]);
      });
    });
  });

  describe('.initialize()', () => {
    test('initializes all layers', () => {
      class TestLayer extends BaseLayer {
        called?: boolean;
        setupKernels() {
          this.called = true;
        }
      }

      const net = new FeedForward({
        inputLayer: () => new TestLayer(),
        hiddenLayers: [
          () => new TestLayer(),
          () => new TestLayer(),
          () => new TestLayer(),
        ],
        outputLayer: () => new TestLayer(),
      });
      net.initialize();

      const layers = net.layers as ILayer[];
      expect(layers.length).toBe(5);
      expect(layers.map((l) => l.constructor !== undefined)).toEqual([
        true,
        true,
        true,
        true,
        true,
      ]);
    });

    test('populates praxis on all layers when it is null', () => {
      class TestLayer extends layerTypes.Model {
        called?: boolean;
        setupKernels() {
          this.called = true;
        }
      }

      const net = new FeedForward({
        inputLayer: () => new TestLayer(),
        hiddenLayers: [
          () => new TestLayer(),
          () => new TestLayer(),
          () => new TestLayer(),
        ],
        outputLayer: () => new TestLayer(),
      });
      net.initialize();

      const layers = net.layers as ILayer[];
      expect(layers.length).toBe(5);
      expect(layers.map((l) => (l as TestLayer).called)).toEqual([
        true,
        true,
        true,
        true,
        true,
      ]);
      expect(layers.map((l) => Boolean(l.praxis))).toEqual([
        true,
        true,
        true,
        true,
        true,
      ]);
    });
    test('populates praxis when defined as setting on layer', () => {
      class TestLayer extends BaseLayer {
        called?: boolean;
        setupKernels() {
          this.called = true;
        }
      }

      const praxis = mockPraxis(mockLayer({}));
      const net = new FeedForward({
        inputLayer: () => new TestLayer(),
        hiddenLayers: [
          (l: ILayer) => new TestLayer({ initPraxis: () => praxis }),
          () => new TestLayer(),
          () => new TestLayer(),
        ],
        outputLayer: () => new TestLayer(),
      });
      net.initialize();

      const layers = net.layers as ILayer[];
      expect(layers.length).toBe(5);
      expect(layers.map((l) => (l as TestLayer).called)).toEqual([
        true,
        true,
        true,
        true,
        true,
      ]);
      expect(layers.map((l) => l.praxis === praxis)).toEqual([
        false,
        true,
        false,
        false,
        false,
      ]);
    });
  });

  describe('.runInput()', () => {
    test('calls .predict() on all layers', () => {
      class TestLayer extends BaseLayer {
        // eslint-disable-next-line
        setupKernels() {}

        called?: boolean;
        predict() {
          this.called = true;
        }
      }

      const net = new FeedForward({
        inputLayer: () => new TestLayer(),
        hiddenLayers: [
          () => new TestLayer(),
          () => new TestLayer(),
          () => new TestLayer(),
        ],
        outputLayer: () => new TestLayer(),
      });

      net.initialize();
      net.runInput();

      const layers = net.layers as ILayer[];
      expect(layers.map((l) => (l as TestLayer).called)).toEqual([
        true,
        true,
        true,
        true,
        true,
      ]);
    });
  });

  describe('._calculateDeltas()', () => {
    test('calls .compare() on all layers', () => {
      class TestLayer extends BaseLayer {
        // eslint-disable-next-line
        setupKernels() {}

        // eslint-disable-next-line
        predict() {}

        called?: boolean;
        compare() {
          this.called = true;
        }
      }

      const net = new FeedForward({
        inputLayer: () => new TestLayer(),
        hiddenLayers: [
          () => new TestLayer(),
          () => new TestLayer(),
          () => new TestLayer(),
        ],
        outputLayer: () => new TestLayer(),
      });

      net.initialize();
      net._calculateDeltas();

      const layers = net.layers as ILayer[];
      expect(layers.map((l) => (l as TestLayer).called)).toEqual([
        true,
        true,
        true,
        true,
        true,
      ]);
    });
  });

  describe('.adjustWeights()', () => {
    test('calls .learn() on all layers', () => {
      class TestLayer extends layerTypes.Model {
        // eslint-disable-next-line
        setupKernels() {}

        // eslint-disable-next-line
        predict() {}

        // eslint-disable-next-line
        compare() {}

        called?: boolean;
        learn() {
          this.called = true;
        }
      }

      const net = new FeedForward({
        inputLayer: () => new TestLayer(),
        hiddenLayers: [
          () => new TestLayer(),
          () => new TestLayer(),
          () => new TestLayer(),
        ],
        outputLayer: () => new TestLayer(),
      });

      net.initialize();
      net.adjustWeights();

      const layers = net.layers as ILayer[];
      expect(layers.map((l) => (l as TestLayer).called)).toEqual([
        true,
        true,
        true,
        true,
        true,
      ]);
    });
  });

  describe('.toJSON()', () => {
    test('can serialize to json', () => {
      class TestInputLayer extends BaseLayer {
        constructor(settings: ILayerSettings) {
          super(settings);
          this.weights = new Float32Array([0, 1, 3, 4, 5, 6, 7, 8, 9]);
        }
      }
      class TestLayer1 extends BaseLayer {
        inputLayer: ILayer;
        constructor(settings: ILayerSettings, inputLayer: ILayer) {
          super(settings);
          this.inputLayer = inputLayer;
        }

        // eslint-disable-next-line
        setupKernels() {}
      }

      class TestLayer2 extends BaseLayer {
        inputLayer: ILayer;
        constructor(settings: ILayerSettings, inputLayer: ILayer) {
          super(settings);
          this.inputLayer = inputLayer;
        }

        // eslint-disable-next-line
        setupKernels() {}
      }

      class TestOperatorLayer extends BaseLayer {
        inputLayer1: ILayer;
        inputLayer2: ILayer;
        constructor(inputLayer1: ILayer, inputLayer2: ILayer) {
          super();
          this.inputLayer1 = inputLayer1;
          this.inputLayer2 = inputLayer2;
        }

        // eslint-disable-next-line
        setupKernels() {}
      }

      class TestOutputLayer extends BaseLayer {
        inputLayer: ILayer;
        constructor(settings: ILayerSettings, inputLayer: ILayer) {
          super(settings);
          this.inputLayer = inputLayer;
        }
      }

      const net = new FeedForward({
        inputLayer: () => new TestInputLayer({ width: 10, height: 1 }),
        hiddenLayers: [
          (inputParam) =>
            new TestOperatorLayer(
              new TestLayer1({}, inputParam),
              new TestLayer2({}, inputParam)
            ),
        ],
        outputLayer: (inputParam) =>
          new TestOutputLayer({ width: 10, height: 5 }, inputParam),
      });
      net.initialize();

      const json = net.toJSON();

      expect(json.layers).toBeDefined();
      expect(json.layers.every((l) => !l.hasOwnProperty('deltas'))).toBe(true);
      expect(json.layers.length).toBe(5);
      expect(json.layers[0]).toEqual({
        type: 'TestInputLayer',
        praxisOpts: null,
        weights: [0, 1, 3, 4, 5, 6, 7, 8, 9],
        width: 10,
        height: 1,
        depth: 0,
      });
      expect(json.layers[1]).toEqual({
        type: 'TestLayer1',
        praxisOpts: null,
        weights: null,
        inputLayerIndex: 0,
        width: 1,
        height: 1,
        depth: 0,
      });
      expect(json.layers[2]).toEqual({
        type: 'TestLayer2',
        praxisOpts: null,
        weights: null,
        inputLayerIndex: 0,
        width: 1,
        height: 1,
        depth: 0,
      });
      expect(json.layers[3]).toEqual({
        type: 'TestOperatorLayer',
        praxisOpts: null,
        weights: null,
        inputLayer1Index: 1,
        inputLayer2Index: 2,
        width: 1,
        height: 1,
        depth: 0,
      });
      expect(json.layers[4]).toEqual({
        height: 5,
        inputLayerIndex: 3,
        type: 'TestOutputLayer',
        weights: null,
        praxisOpts: null,
        width: 10,
        depth: 0,
      });
    });
  });

  describe('.fromJSON()', () => {
    test('can deserialize to object from json using inputLayerIndex', () => {
      class TestLayer extends BaseLayer implements ILayer {
        inputLayer?: ILayer;
        constructor(settings: ILayerSettings, inputLayer?: ILayer) {
          super(settings);
          this.inputLayer = inputLayer;
        }

        // eslint-disable-next-line
        setupKernels() {}
      }

      const net = FeedForward.fromJSON(
        {
          type: '',
          sizes: [],
          inputLayerIndex: 0,
          outputLayerIndex: 3,
          layers: [
            {
              type: 'TestLayer',
            },
            {
              type: 'TestLayer',
              inputLayerIndex: 0,
            },
            {
              type: 'TestLayer',
              inputLayerIndex: 1,
            },
            {
              type: 'TestLayer',
              inputLayerIndex: 2,
            },
          ],
        },
        (jsonLayer: ILayerJSON, inputLayer1?: ILayer, inputLayer2?: ILayer) => {
          switch (jsonLayer.type) {
            case 'TestLayer':
              return new TestLayer(jsonLayer, inputLayer1);
            default:
              throw new Error(`unknown layer ${jsonLayer.type}`);
          }
        }
      );

      const layers = net.options.layers as ILayer[];
      expect(layers.map((l) => l instanceof TestLayer)).toEqual([
        true,
        true,
        true,
        true,
      ]);
      expect(layers.map((l) => l.inputLayer instanceof TestLayer)).toEqual([
        false,
        true,
        true,
        true,
      ]);
    });

    test('can deserialize to object from json using inputLayer1Index & inputLayer2Index', () => {
      class TestLayer extends BaseLayer {
        static get defaults() {
          return { foo: null };
        }

        inputLayer?: ILayer;
        constructor(settings: ILayerSettings, inputLayer?: ILayer) {
          super(settings);
          this.inputLayer = inputLayer;
        }

        // eslint-disable-next-line
        setupKernels() {}
      }

      class TestOperatorLayer extends BaseLayer {
        static get defaults() {
          return { foo: null };
        }

        inputLayer1?: ILayer;
        inputLayer2?: ILayer;
        constructor(
          settings: ILayerSettings,
          inputLayer1?: ILayer,
          inputLayer2?: ILayer
        ) {
          super(settings);
          this.inputLayer1 = inputLayer1;
          this.inputLayer2 = inputLayer2;
        }

        // eslint-disable-next-line
        setupKernels() {}
      }

      const net = FeedForward.fromJSON(
        {
          sizes: [],
          type: '',
          inputLayerIndex: 0,
          outputLayerIndex: 2,
          layers: [
            {
              type: 'TestLayer',
            },
            {
              type: 'TestLayer',
              inputLayerIndex: 0,
            },
            {
              type: 'TestOperatorLayer',
              inputLayer1Index: 0,
              inputLayer2Index: 1,
            },
          ],
        },
        (jsonLayer: ILayerJSON, inputLayer1?: ILayer, inputLayer2?: ILayer) => {
          switch (jsonLayer.type) {
            case 'TestLayer':
              return new TestLayer(jsonLayer, inputLayer1);
            case 'TestOperatorLayer':
              return new TestOperatorLayer(jsonLayer, inputLayer1, inputLayer2);
            default:
              throw new Error(`unknown layer ${jsonLayer.type}`);
          }
        }
      );

      const layers = net.options.layers as ILayer[];
      expect(layers.length).toBe(3);
      expect(layers[0] instanceof TestLayer).toBeTruthy();
      expect(layers[0] instanceof TestLayer).toBeTruthy();
      expect(layers[1] instanceof TestLayer).toBeTruthy();
      expect(layers[2] instanceof TestOperatorLayer).toBeTruthy();
      expect(layers[2].inputLayer1).toEqual(layers[0]);
      expect(layers[2].inputLayer2).toEqual(layers[1]);
    });
  });

  describe('._trainPattern()', () => {
    let runInputSpy: jest.SpyInstance;
    let _calculateDeltasSpy: jest.SpyInstance;
    let adjustWeightsSpy: jest.SpyInstance;
    beforeEach(() => {
      runInputSpy = jest.spyOn(FeedForward.prototype, 'runInput');
      _calculateDeltasSpy = jest.spyOn(
        FeedForward.prototype,
        '_calculateDeltas'
      );
      adjustWeightsSpy = jest.spyOn(FeedForward.prototype, 'adjustWeights');
    });
    afterEach(() => {
      runInputSpy.mockRestore();
      _calculateDeltasSpy.mockRestore();
      adjustWeightsSpy.mockRestore();
    });
    test('calls training methods and mse2d and returns value', () => {
      const net = new FeedForward({
        inputLayer: () => input({ height: 1 }),
        hiddenLayers: [(inputLayer) => feedForward({ height: 1 }, inputLayer)],
        outputLayer: (inputLayer) => output({ height: 1 }, inputLayer),
      });
      net.initialize();
      net._outputLayer = mockLayer({});
      net._outputLayer.errors = [0];

      net._trainPattern([1], [3], true);

      expect(runInputSpy).toHaveBeenCalled();
      expect(_calculateDeltasSpy).toHaveBeenCalled();
      expect(adjustWeightsSpy).toHaveBeenCalled();
    });
  });
  describe('.trainOpts', () => {
    let net: FeedForward;
    let _calculateTrainingErrorSpy: SpyInstance;
    beforeEach(() => {
      const layer1 = mockLayer({ width: 1, height: 1 });
      const layer2 = mockLayer({ width: 1, height: 1 });
      layer2.errors = [1];
      net = new FeedForward({
        inputLayerIndex: 0,
        layers: [layer1, layer2],
        outputLayerIndex: 1,
      });
      _calculateTrainingErrorSpy = jest.spyOn(net, '_calculateTrainingError');
    });
    afterEach(() => {
      _calculateTrainingErrorSpy.mockRestore();
    });
    test('.errorCheckInterval', () => {
      const trainOpts = {
        iterations: 2,
        errorCheckInterval: 1,
        errorThresh: 0.5,
      };
      const mockData = [{ input: [1, 1], output: [1] }];
      net.train(mockData, trainOpts);
      expect(_calculateTrainingErrorSpy).toHaveBeenCalled();
    });
  });
});


================================================
FILE: src/index.ts
================================================
import * as activation from './activation';
import { AE } from './autoencoder';
import CrossValidate from './cross-validate';
import { FeedForward } from './feed-forward';
import * as layer from './layer';
import { layerTypes } from './layer';
import { likely } from './likely';
import { lookup } from './lookup';
import { NeuralNetwork } from './neural-network';
import { NeuralNetworkGPU } from './neural-network-gpu';
import * as praxis from './praxis';
import { Recurrent } from './recurrent';
import { GRU } from './recurrent/gru';
import { GRUTimeStep } from './recurrent/gru-time-step';
import { LSTM } from './recurrent/lstm';
import { LSTMTimeStep } from './recurrent/lstm-time-step';
import { RNN } from './recurrent/rnn';
import { RNNTimeStep } from './recurrent/rnn-time-step';
import { DataFormatter } from './utilities/data-formatter';
import { max } from './utilities/max';
import { mse } from './utilities/mse';
import { ones, ones2D } from './utilities/ones';
import * as random from './utilities/random';
import { randomWeight } from './utilities/random-weight';
import { randos } from './utilities/randos';
import { range } from './utilities/range';
import { toArray } from './utilities/to-array';
import { toSVG } from './utilities/to-svg';
import { zeros } from './utilities/zeros';

const recurrent = {
  RNNTimeStep,
  LSTMTimeStep,
  GRUTimeStep,
  RNN,
  LSTM,
  GRU,
};

const utilities = {
  max,
  mse,
  ones,
  ones2D,
  random,
  randomWeight,
  randos,
  range,
  toArray,
  DataFormatter,
  zeros,
  toSVG,
};

export {
  activation,
  AE,
  CrossValidate,
  likely,
  layer,
  layerTypes,
  lookup,
  praxis,
  FeedForward,
  NeuralNetwork,
  NeuralNetworkGPU,
  Recurrent,
  recurrent,
  utilities,
};


================================================
FILE: src/layer/README.md
================================================
# Layer

## Basics
### Memory
A "basic layer" is composed of three types of Matrices which store what the neural network understand, its memory.
* `weights` - how a layer forward propagates, or `predicts`. Usually weights initialize as random numbers and are
* `errors` - how a network knows how far it was from an input or `target` during back propagation
* `deltas` - how a network knows to adjust its `weights` during back propagation

### Action 
A layer has three different operations for it to "learn" 
* `predict` - usually referred to by non-mortals as "forward propagation", this is where `weights` are used
* `compare` - the first of two steps in "back propagation", this compares what a network predicted to a `target` to calculate `deltas` and `errors`
* `learn` - the second step in "back propagation", this step used to update the `weights` from what was measured from `deltas` and `errors` during `compare`


### Layer Composition
A layer can be very simple, like `Random` or `Add`, but "compound layers" can also be described as "layers of layers".
Layer Example:
```js
import { FeedForward, layer } from 'brain.js';
const { input, output, add, random } = layer;

function mySuperLayer(input) {
  return add(random(), input);
}
```

Usage example:
```js
const net = new FeedForward({
  inputLayer: () => input(),
  hiddenLayers: [
    input => mySuperLayer(input)
  ],
  outputLayer: input => output(input)
});
```
In this example both `add` and `random` are composed together, ie `layer composition`.  This simple means of composing
layers and in turn networks works with both simple (feed forward) or more complex (recurrent) networks.



================================================
FILE: src/layer/activation.test.ts
================================================
import { Activation } from './activation';
import { ILayerSettings } from './base-layer';
import { mockPraxis, mockLayer } from '../test-utils';

describe('Activation Abstract Layer', () => {
  describe('.constructor()', () => {
    describe('calls .validate()', () => {
      let mockValidate: jest.SpyInstance;
      beforeEach(() => {
        mockValidate = jest.spyOn(Activation.prototype, 'validate');
      });
      afterEach(() => {
        mockValidate.mockRestore();
      });
      test('.validate() call', () => {
        const mockInputLayer = mockLayer({
          width: 1,
          height: 1,
          weights: [new Float32Array(1).fill(1)],
          deltas: [new Float32Array(1)],
        });
        const praxis = mockPraxis(mockInputLayer);
        const l = new Activation(mockInputLayer, {
          praxis,
        });
        expect(l.validate).toBeCalled();
      });
    });
    test('inputLayer', () => {
      const mockInputLayer = mockLayer({
        width: 1,
        height: 1,
        weights: [new Float32Array(1).fill(1)],
        deltas: [new Float32Array(1)],
      });
      const l = new Activation(mockInputLayer);
      expect(l.inputLayer).toBe(mockInputLayer);
    });
    test('dimensions', () => {
      const width = 3;
      const height = 4;
      const depth = 5;
      const testInputLayer = mockLayer({ width, height, depth });
      const l = new Activation(testInputLayer);
      expect(l.width).toBe(width);
      expect(l.height).toBe(height);
      expect(l.depth).toBe(depth);
    });
    test('2d weights & deltas', () => {
      const width = 3;
      const height = 4;
      const testInputLayer = mockLayer({ width, height });
      const l = new Activation(testInputLayer);
      const weights = l.weights as Float32Array[];
      expect(weights.length).toBe(height);
      expect(weights[0].length).toBe(width);
      expect(typeof weights[0][0]).toBe('number');
    });
    test('3d weights & deltas', () => {
      const width = 3;
      const height = 4;
      const depth = 5;
      const testInputLayer = mockLayer({ width, height, depth });
      const l = new Activation(testInputLayer);
      const weights = l.weights as Float32Array[][];
      expect(weights.length).toBe(depth);
      expect(weights[0].length).toBe(height);
      expect(weights[0][0].length).toBe(width);
      expect(typeof weights[0][0][0]).toBe('number');
    });
    test('initPraxis', () => {
      const mockPraxisInstance = mockPraxis(mockLayer({}));
      const mockInitPraxis = jest.fn(() => mockPraxisInstance);
      const settings: ILayerSettings = {
        initPraxis: mockInitPraxis,
        praxisOpts: {},
      };
      const mockInputLayer = mockLayer({
        width: 1,
        height: 1,
      });
      const l = new Activation(mockInputLayer, settings);
      expect(mockInitPraxis).toBeCalled();
      expect(l.praxis).toBe(mockPraxisInstance);
    });
  });
});


================================================
FILE: src/layer/activation.ts
================================================
import { BaseLayer, ILayerSettings, ILayer } from './base-layer';
import { zeros2D } from '../utilities/zeros-2d';
import { zeros3D } from '../utilities/zeros-3d';

export type ActivationType = new (
  inputLayer: ILayer,
  settings: Partial<ILayerSettings>
) => ILayer;

export class Activation extends BaseLayer {
  inputLayer: ILayer;

  get width(): number {
    return this.inputLayer.width;
  }

  get height(): number {
    return this.inputLayer.height;
  }

  get depth(): number {
    return this.inputLayer.depth;
  }

  constructor(inputLayer: ILayer, settings?: Partial<ILayerSettings>) {
    super(settings);
    this.inputLayer = inputLayer;
    const { width, height, depth } = this;
    this.predictKernel = null;
    this.compareKernel = null;
    this.validate();
    if (depth > 0) {
      this.weights = zeros3D(width, height, depth);
      this.deltas = zeros3D(width, height, depth);
    } else if (height > 0) {
      this.weights = zeros2D(width, height);
      this.deltas = zeros2D(width, height);
    }
    this.setupPraxis();
  }
}


================================================
FILE: src/layer/add.test.ts
================================================
import { GPU } from 'gpu.js';
import { gpuMock } from 'gpu-mock.js';

import { predict, Add, add } from './add';
import { setup, teardown, makeKernel, release } from '../utilities/kernel';
import { checkSameSize } from '../utilities/layer-size';
import { mockLayer, mockPraxis } from '../test-utils';

jest.mock('../utilities/layer-size');
jest.mock('../utilities/kernel', () => {
  return {
    makeKernel: jest.fn((fn) => () => [fn()]),
    setup: jest.fn(),
    release: jest.fn(),
    clear: jest.fn(),
    teardown: jest.fn(),
    clone: jest.fn((matrix: Float32Array[]) =>
      matrix.map((row: Float32Array) => row.slice(0))
    ),
  };
});

describe('Add Layer', () => {
  beforeEach(() => {
    setup(
      new GPU({
        mode: 'cpu',
      })
    );
  });
  afterEach(() => {
    teardown();
  });
  describe('.constructor', () => {
    let validateSpy: jest.SpyInstance;
    let setupPraxisSpy: jest.SpyInstance;
    beforeEach(() => {
      validateSpy = jest.spyOn(Add.prototype, 'validate');
      setupPraxisSpy = jest.spyOn(Add.prototype, 'setupPraxis');
    });
    afterEach(() => {
      validateSpy.mockRestore();
      setupPraxisSpy.mockRestore();
    });
    it('sets up the instance', () => {
      const mockInputLayer1 = mockLayer({ width: 1, height: 3 });
      const mockInputLayer2 = mockLayer({ width: 1, height: 3 });
      const praxis = mockPraxis(mockLayer({}));
      const settings = {
        praxis,
      };
      const add = new Add(mockInputLayer1, mockInputLayer2, settings);
      expect(add.inputLayer1).toBe(mockInputLayer1);
      expect(add.inputLayer2).toBe(mockInputLayer2);
      expect(add.validate).toBeCalled();
      expect(add.setupPraxis).toBeCalled();
      expect(add.width).toBe(1);
      expect(add.height).toBe(3);
    });
  });
  describe('.predict (forward propagation)', () => {
    test('releases this.weights', () => {
      const praxis = mockPraxis(mockLayer({}));
      const add = new Add(
        mockLayer({ width: 1, height: 1, weights: [new Float32Array(1)] }),
        mockLayer({ width: 1, height: 1, weights: [new Float32Array(1)] }),
        {
          praxis,
        }
      );
      add.predictKernel = makeKernel(
        function (weights1: number[][], weights2: number[][]) {
          return 1;
        },
        { output: [1, 1] }
      );
      const mockWeights = (add.weights = [new Float32Array(1)]);
      add.predict();
      expect(release).toHaveBeenCalledWith(mockWeights);
    });
    test('can add a simple matrix', () => {
      const inputs1 = [
        [1, 2, 3],
        [4, 5, 6],
        [7, 8, 9],
      ];
      const inputs2 = [
        [1, 2, 3],
        [4, 5, 6],
        [7, 8, 9],
      ];
      const results = gpuMock(predict, {
        output: [3, 3],
      })(inputs1, inputs2);

      expect(results).toEqual([
        new Float32Array([2, 4, 6]),
        new Float32Array([8, 10, 12]),
        new Float32Array([14, 16, 18]),
      ]);
    });
  });
  describe('.validate', () => {
    it('calls LayerSize.checkSameSize()', () => {
      const mockLayer1 = {
        height: 1,
        width: 1,
      };
      const mockLayer2 = {
        height: 1,
        width: 1,
      };
      Add.prototype.validate.apply({
        inputLayer1: mockLayer1,
        inputLayer2: mockLayer2,
      });
      expect(checkSameSize).toHaveBeenCalledWith(mockLayer1, mockLayer2);
    });
  });
  describe('.setupKernels', () => {
    it('defines this.predictKernel', () => {
      const mockInstance = {
        width: 1,
        height: 1,
        predictKernel: null,
      };
      Add.prototype.setupKernels.apply(mockInstance);
      expect(makeKernel).toHaveBeenCalledWith(predict, {
        output: [1, 1],
        immutable: true,
      });
      expect(mockInstance.predictKernel).not.toBe(null);
    });
  });
  describe('.predict', () => {
    it('calls this.predictKernel with correct arguments', () => {
      const mockWeights1 = {};
      const mockWeights2 = {};
      const mockPredictKernel = jest.fn();
      const mockInstance = {
        inputLayer1: { weights: mockWeights1 },
        inputLayer2: { weights: mockWeights2 },
        predictKernel: mockPredictKernel,
      };
      Add.prototype.predict.apply(mockInstance);
      expect(mockPredictKernel).toHaveBeenCalled();
    });
    it('defined this.weights from this.predictKernel', () => {
      const mockWeights1 = {};
      const mockWeights2 = {};
      const mockResult = {};
      const mockPredictKernel = () => mockResult;
      const mockInstance = {
        inputLayer1: { weights: mockWeights1 },
        inputLayer2: { weights: mockWeights2 },
        predictKernel: mockPredictKernel,
        weights: null,
      };
      Add.prototype.predict.apply(mockInstance);
      expect(mockInstance.weights).toBe(mockResult);
    });
  });
  describe('.compare', () => {
    beforeEach(() => {
      jest.unmock('../../src/utilities/kernel');
    });
    it('sets this.inputLayer1.deltas & this.inputLayer2.deltas from this.deltas', () => {
      const mockDeltas = [new Float32Array([1])];
      const mockInstance = {
        deltas: mockDeltas,
        inputLayer1: {
          deltas: null,
        },
        inputLayer2: {
          deltas: null,
        },
      };
      Add.prototype.compare.apply(mockInstance);
      expect(mockInstance.inputLayer1.deltas).toEqual(mockDeltas);
      expect(mockInstance.inputLayer2.deltas).toEqual(mockDeltas);
    });
  });
  describe('add()', () => {
    let setupPraxisMock: jest.SpyInstance;
    beforeEach(() => {
      setupPraxisMock = jest.spyOn(Add.prototype, 'setupPraxis');
    });
    afterEach(() => {
      setupPraxisMock.mockRestore();
    });
    it('instantiates Add with inputLayer1, inputLayer2, and settings', () => {
      const mockInputLayer1 = mockLayer({
        width: 1,
        height: 1,
      });
      const mockInputLayer2 = mockLayer({
        width: 1,
        height: 1,
      });
      const praxis = mockPraxis(mockLayer({}));
      const settings = {
        praxis,
      };
      const layer = add(mockInputLayer1, mockInputLayer2, settings);
      expect(layer.inputLayer1).toBe(mockInputLayer1);
      expect(layer.inputLayer2).toBe(mockInputLayer2);
      expect(layer.setupPraxis).toHaveBeenCalled();
    });
  });
});


================================================
FILE: src/layer/add.ts
================================================
import { makeKernel, release, clone } from '../utilities/kernel';
import { checkSameSize } from '../utilities/layer-size';
import { Operator } from './operator';
import { IKernelFunctionThis, IKernelRunShortcut, Texture } from 'gpu.js';
import { ILayerSettings, ILayer } from './base-layer';

export function predict(
  this: IKernelFunctionThis,
  inputWeights1: number[][],
  inputWeights2: number[][]
): number {
  return (
    inputWeights1[this.thread.y][this.thread.x] +
    inputWeights2[this.thread.y][this.thread.x]
  );
}

export class Add extends Operator {
  get width(): number {
    return this.inputLayer1.width;
  }

  get height(): number {
    return this.inputLayer1.height;
  }

  get depth(): number {
    return this.inputLayer1.depth;
  }

  validate(): void {
    super.validate();
    checkSameSize(this.inputLayer1, this.inputLayer2);
  }

  setupKernels(): void {
    this.predictKernel = makeKernel(predict, {
      output: [this.width, this.height],
      immutable: true,
    });
  }

  predict(): void {
    release(this.weights);
    this.weights = (this.predictKernel as IKernelRunShortcut)(
      this.inputLayer1.weights,
      this.inputLayer2.weights
    ) as Texture;
  }

  compare(): void {
    // TODO: Do we need release and clone here?
    release(this.inputLayer1.deltas);
    release(this.inputLayer2.deltas);
    this.inputLayer1.deltas = clone(this.deltas);
    this.inputLayer2.deltas = clone(this.deltas);
  }
}

export function add(
  inputLayer1: ILayer,
  inputLayer2: ILayer,
  settings?: ILayerSettings
): Add {
  return new Add(inputLayer1, inputLayer2, settings);
}


================================================
FILE: src/layer/arthur-feed-forward.ts
================================================
import {
  ArthurDeviationWeights,
  arthurDeviationWeights,
  IArthurDeviationWeightsSettings,
} from '../praxis/arthur-deviation-weights';
import {
  arthurDeviationBiases,
  IArthurDeviationBiasesSettings,
} from '../praxis/arthur-deviation-biases';
import { ILayer } from './base-layer';
import { add } from './add';
import { IRandomSettings, random } from './random';
import { multiply } from './multiply';
import { Sigmoid, sigmoid } from './sigmoid';
import { IPraxis } from '../praxis/base-praxis';

export interface IArthurFeedForwardPraxisSettings
  extends IArthurDeviationBiasesSettings,
    IArthurDeviationWeightsSettings {}

export interface IArthurFeedForwardSettings extends IRandomSettings {
  initPraxis?: (
    layerTemplate: ILayer,
    settings?: IArthurFeedForwardPraxisSettings | null
  ) => IPraxis;
}

export function arthurFeedForward(
  settings: IArthurFeedForwardPraxisSettings,
  inputLayer: ILayer
): Sigmoid {
  const { height } = settings;
  function initWeightsPraxis(
    layerTemplate: ILayer,
    settings?: IArthurDeviationWeightsSettings
  ): IPraxis {
    const praxis = arthurDeviationWeights(layerTemplate, settings);
    praxis.setupKernels();
    return praxis;
  }
  function initBiasesPraxis(
    layerTemplate: ILayer,
    settings?: IArthurDeviationBiasesSettings
  ): IPraxis {
    const praxis = arthurDeviationBiases(layerTemplate, settings);
    praxis.setupKernels();
    return praxis;
  }
  const weightsLayer = random({
    id: 'weights',
    height,
    width: inputLayer.height,
    initPraxis: initWeightsPraxis,
  });

  const biasesLayer = random({
    id: 'biases',
    height,
    initPraxis: initBiasesPraxis,
  });

  const multiplyLayer = multiply(weightsLayer, inputLayer);
  const addLayer = add(multiplyLayer, biasesLayer);
  const sigmoidLayer = sigmoid(addLayer);

  const weightsPraxis = weightsLayer.praxis as ArthurDeviationWeights;
  weightsPraxis.weightsLayer = weightsLayer;
  weightsPraxis.incomingLayer = inputLayer;
  weightsPraxis.deltaLayer = sigmoidLayer;
  return sigmoidLayer;
}


================================================
FILE: src/layer/base-layer.test.ts
================================================
import { ILayer } from './base-layer';
import { IPraxis, IPraxisSettings } from '../praxis/base-praxis';
import { mockLayer, mockPraxis } from '../test-utils';

jest.mock('../utilities/kernel');

describe('BaseLayer Layer', () => {
  describe('dimensions', () => {
    describe('when given undefined for width, height, and depth', () => {
      test('automatically assigns 1 to width, height, and depth', () => {
        const base = mockLayer({
          initPraxis: (
            layerTemplate: ILayer,
            praxisSettings?: IPraxisSettings
          ): IPraxis => mockPraxis(layerTemplate, praxisSettings),
          praxisOpts: {},
        });

        expect(base.width).toBe(1);
        expect(base.height).toBe(1);
        expect(base.depth).toBe(null);
      });
    });
  });

  describe('.praxisOpts', () => {
    test('are inherited to .praxis() call', () => {
      const initPraxis = jest.fn();
      interface IPraxisExtendedSettings extends IPraxisSettings {
        test: number;
      }
      const praxisOpts: IPraxisExtendedSettings = {
        test: 100,
      };
      const base = mockLayer({
        initPraxis,
        praxisOpts,
      });
      expect(initPraxis).toHaveBeenCalledWith(base, praxisOpts);
    });
  });
});


================================================
FILE: src/layer/base-layer.ts
================================================
import {
  IKernelRunShortcut,
  Input,
  Kernel,
  KernelOutput,
  Texture,
  TextureArrayOutput,
} from 'gpu.js';
import { IPraxis, IPraxisSettings } from '../praxis/base-praxis';
import { clear } from '../utilities/kernel';

export interface ILayerJSON {
  width?: number;
  height?: number;
  depth?: number;
  weights?: number[] | number[][] | number[][][] | null;
  type: string;
  inputLayerIndex?: number;
  inputLayer1Index?: number;
  inputLayer2Index?: number;
  praxisOpts?: Partial<IPraxisSettings> | null;
}

export interface ILayer {
  width: number;
  height: number;
  depth: number;
  weights: KernelOutput | Input;
  deltas: KernelOutput;
  praxis: IPraxis | null;
  errors?: KernelOutput | null;
  setupKernels: (training?: boolean) => void;
  predictKernel: IKernelRunShortcut | null;
  compareKernel: IKernelRunShortcut | null;
  settings: Partial<ILayerSettings>;
  reuseKernels: (layer: ILayer) => void;
  predict: (inputs?: KernelOutput) => void;
  compare: (targetValues?: KernelOutput) => void;
  learn: ((learningRate?: number) => void) | ((learningRate: number) => void);
  toJSON: () => Partial<ILayerJSON>;
  inputLayer?: ILayer;
  inputLayer1?: ILayer;
  inputLayer2?: ILayer;
  index?: number;
  id?: string;
}

export interface ILayerSettings {
  width?: number | null;
  height?: number | null;
  depth?: number | null;
  weights?: KernelOutput | null;
  deltas?: KernelOutput | null;
  id?: string;
  praxis?: IPraxis | null;
  praxisOpts?: Partial<IPraxisSettings> | null;
  initPraxis?:
    | ((layerTemplate: ILayer, settings?: IPraxisSettings) => IPraxis)
    | null;
  cleanupDeltas?: boolean;
}

export const baseLayerDefaultSettings: ILayerSettings = {
  width: 1,
  height: 1,
  depth: null,
  weights: null,
  deltas: null,
  praxis: null,
  praxisOpts: null,
  cleanupDeltas: true,
};

export type BaseLayerType = new (settings?: Partial<ILayerSettings>) => ILayer;

export class BaseLayer implements ILayer {
  praxis: IPraxis | null = null;
  predictKernel: IKernelRunShortcut | null = null;
  compareKernel: IKernelRunShortcut | null = null;
  settings: Partial<ILayerSettings>;

  get width(): number {
    return this.settings.width ?? 0;
  }

  get height(): number {
    return this.settings.height ?? 0;
  }

  get depth(): number {
    return this.settings.depth ?? 0;
  }

  get weights(): KernelOutput | Input {
    return this.settings.weights as KernelOutput;
  }

  set weights(weights: KernelOutput | Input) {
    this.settings.weights = weights as KernelOutput;
    if (this.settings.cleanupDeltas && this.deltas) {
      clear(this.deltas);
    }
  }

  get deltas(): KernelOutput {
    return this.settings.deltas as KernelOutput;
  }

  set deltas(deltas: KernelOutput) {
    this.settings.deltas = deltas;
  }

  get id(): string {
    return this.settings.id ?? '';
  }

  set id(title: string) {
    this.settings.id = title;
  }

  constructor(settings?: Partial<ILayerSettings>) {
    if (settings) {
      this.settings = { ...baseLayerDefaultSettings, ...settings };
    } else {
      this.settings = { ...baseLayerDefaultSettings };
    }
    this.setupPraxis();
  }

  setupPraxis(): void {
    const { initPraxis, praxis, praxisOpts } = this.settings;
    if (!this.praxis) {
      if (initPraxis) {
        if (praxisOpts) {
          this.praxis = initPraxis(this, praxisOpts);
        } else {
          this.praxis = initPraxis(this);
        }
      } else if (praxis) {
        this.praxis = praxis;
      }
    }
  }

  /*
  get weights() {
    return this._weights;
  }

  set weights(value) {
    if (value) {
      if (value.dimensions) {
        if (value.dimensions[0] !== this.width) {
          throw new Error(`${this.constructor.name}.weights being set with improper value width`);
        }
        if (value.dimensions[1] !== this.height) {
          throw new Error(`${this.constructor.name}.weights being set with improper value height`);
        }
      } else {
        if (value[0].length !== this.width) {
          throw new Error(`${this.constructor.name}.weights being set with improper value width`);
        }
        if (value.length !== this.height) {
          throw new Error(`${this.constructor.name}.weights being set with improper value height`);
        }
      }
    }
    this._weights = value;
  }

  get deltas() {
    return this._deltas;
  }

  set deltas(value) {
    if (value) {
      if (value.dimensions) {
        if (value.dimensions[0] !== this.width) {
          throw new Error(`${this.constructor.name}.deltas being set with improper value width`);
        }
        if (value.dimensions[1] !== this.height) {
          throw new Error(`${this.constructor.name}.deltas being set with improper value height`);
        }
      } else {
        if (value[0].length !== this.width) {
          throw new Error(`${this.constructor.name}.deltas being set with improper value width`);
        }
        if (value.length !== this.height) {
          throw new Error(`${this.constructor.name}.deltas being set with improper value height`);
        }
      }
    }
    this._deltas = value;
  } */

  validate(): void {
    if (Number.isNaN(this.height)) {
      throw new Error(`${this.constructor.name} layer height is not a number`);
    }
    if (Number.isNaN(this.width)) {
      throw new Error(`${this.constructor.name} layer width is not a number`);
    }
    if (this.height < 1) {
      throw new Error(`${this.constructor.name} layer height is less than 1`);
    }
    if (this.width < 1) {
      throw new Error(`${this.constructor.name} layer width is less than 1`);
    }
  }

  setupKernels(isTraining?: boolean): void {}

  reuseKernels(layer: ILayer): void {
    if (layer.width !== this.width) {
      throw new Error(
        `${this.constructor.name} kernel width mismatch ${layer.width} is not ${this.width}`
      );
    }
    if (layer.height !== this.height) {
      throw new Error(
        `${this.constructor.name} kernel width mismatch ${layer.height} is not ${this.height}`
      );
    }
    if (layer.hasOwnProperty('predictKernel') && layer.predictKernel !== null) {
      if (!(layer.predictKernel as Kernel).immutable) {
        throw new Error(
          `${layer.constructor.name}.predictKernel is not reusable, set kernel.immutable = true`
        );
      }
      this.predictKernel = layer.predictKernel;
    }
    if (layer.hasOwnProperty('compareKernel') && layer.compareKernel !== null) {
      if (!(layer.compareKernel as Kernel).immutable) {
        throw new Error(
          `${layer.constructor.name}.compareKernel is not reusable, set kernel.immutable = true`
        );
      }
      this.compareKernel = layer.compareKernel;
    }
    this.praxis = layer.praxis;
  }

  predict(inputs?: KernelOutput): void {}

  compare(targetValues?: KernelOutput): void {}

  learn(learningRate?: number): void {}

  toArray(): TextureArrayOutput {
    return Array.isArray(this.weights)
      ? this.weights
      : (this.weights as Texture).toArray();
  }

  toJSON(): Partial<ILayerJSON> {
    return BaseLayer.toJSON(this);
  }

  static toJSON(layer: ILayer): Partial<ILayerJSON> {
    const { weights } = layer;
    return {
      width: layer.width,
      height: layer.height,
      depth: layer.depth,
      weights: toUntypedArray(
        (weights && weights instanceof Texture
          ? weights.toArray()
          : weights) as
          | Float32Array
          | Float32Array[]
          | Float32Array[][]
          | number[]
          | number[][]
          | number[][][]
          | null
      ),
      type: layer.constructor.name,
      praxisOpts: layer.praxis ? layer.praxis.toJSON() : null,
    };
  }
}

function toUntypedArray(
  weights:
    | Float32Array
    | Float32Array[]
    | Float32Array[][]
    | number[]
    | number[][]
    | number[][][]
    | null
): number[][][] | number[][] | number[] | null {
  if (weights === null) return null;
  if (Array.isArray(weights)) {
    if (typeof weights[0] === 'number') {
      return weights as number[];
    } else if (Array.isArray(weights[0]) && typeof weights[0][0] === 'number') {
      return weights as number[][];
    } else if (
      Array.isArray(weights[0][0]) &&
      typeof weights[0][0][0] === 'number'
    ) {
      return weights as number[][][];
    } else if (weights[0] instanceof Float32Array) {
      const matrix = weights as Float32Array[];
      return matrix.map((row: Float32Array) => {
        return Array.from(row);
      });
    } else if (weights[0][0] instanceof Float32Array) {
      const cube = weights as Float32Array[][];
      return cube.map((matrix: Float32Array[]): number[][] => {
        return matrix.map((row: Float32Array): number[] => {
          return Array.from(row);
        });
      });
    }
  } else if (weights) {
    return Array.from(weights);
  }
  throw new Error('unexpected value');
}


================================================
FILE: src/layer/convolution.test.ts
================================================
import { GPU } from 'gpu.js';
import { gpuMock } from 'gpu-mock.js';
import {
  predict,
  compareFilterDeltas,
  compareInputDeltas,
  compareBiases,
} from './convolution';
import { setup, teardown } from '../utilities/kernel';
import { onePlusPlus3D } from '../test-utils';

describe('Convolution Layer', () => {
  beforeEach(() => {
    setup(
      new GPU({
        mode: 'cpu',
      })
    );
  });
  afterEach(() => {
    teardown();
  });
  describe('.predict (forward propagation)', () => {
    test('can convolve a simple matrix', () => {
      const inputs = [
        [
          [1, 2, 3],
          [4, 5, 6],
          [7, 8, 9],
        ],
      ];
      const filters = [
        [
          [1, 2, 3],
          [4, 5, 6],
          [7, 8, 9],
        ],
      ];
      const biases = [1, 2, 3];

      const results = gpuMock(predict, {
        output: [3, 3],
        constants: {
          strideX: 1,
          strideY: 1,
          paddingY: 0,
          paddingX: 0,
          filterHeight: 3,
          filterWidth: 3,
          filterCount: 1,
          inputWidth: 3,
          inputHeight: 3,
          inputDepth: 1,
        },
      })(filters, inputs, biases);

      expect(results).toEqual([
        new Float32Array([286, 187, 91]),
        new Float32Array([155, 95, 43]),
        new Float32Array([51, 27, 10]),
      ]);
    });
  });

  describe('.compareFilterDeltas (back propagation)', () => {
    test('can convolve a simple matrix', () => {
      const filterWidth = 2;
      const filterHeight = 2;
      const inputWidth = 4;
      const inputHeight = 4;
      const inputDepth = 1;
      const width = 2;
      const height = 2;
      const depth = 1;
      const stride = 1;
      const padding = 0;

      const filterDeltas = onePlusPlus3D(filterWidth, filterHeight, inputDepth);
      const inputs = onePlusPlus3D(inputWidth, inputHeight, inputDepth);
      const deltas = onePlusPlus3D(width, height, depth);
      const results = gpuMock(compareFilterDeltas, {
        output: [filterWidth, filterHeight, 1],
        constants: {
          strideX: stride,
          strideY: stride,
          paddingY: padding,
          paddingX: padding,
          filterWidth,
          filterHeight,
          inputWidth,
          inputHeight,
          deltaZ: 0,
          deltaWidth: width,
          deltaHeight: height,
        },
      })(filterDeltas, inputs, deltas);

      expect(results).toEqual([
        [new Float32Array([45, 56]), new Float32Array([87, 98])],
      ]);
    });
  });

  describe('.compareInputDeltas (back propagation)', () => {
    test('can convolve a simple matrix', () => {
      const inputDeltas = [
        [
          [1, 2, 3],
          [4, 5, 6],
          [7, 8, 9],
        ],
      ];
      const filters = [
        [
          [1, 2, 3],
          [4, 5, 6],
          [7, 8, 9],
        ],
      ];
      const deltas = [
        [
          [1, 2, 3],
          [4, 5, 6],
          [7, 8, 9],
        ],
      ];
      const results = gpuMock(compareInputDeltas, {
        output: [3, 3],
        constants: {
          strideX: 1,
          strideY: 1,
          paddingY: 0,
          paddingX: 0,
          filterHeight: 3,
          filterWidth: 3,
          filterCount: 1,
          deltaWidth: 3,
          deltaHeight: 3,
          deltaDepth: 1,
          deltaZ: 0,
        },
      })(inputDeltas, filters, deltas);

      expect(results).toEqual([
        new Float32Array([2, 6, 13]),
        new Float32Array([12, 31, 62]),
        new Float32Array([37, 92, 174]),
      ]);
    });
  });

  describe('.compareBiases (back propagation)', () => {
    const deltas = [
      [
        [0, 16],
        [8, 24],
      ],
      [
        [1, 17],
        [9, 25],
      ],
      [
        [2, 18],
        [10, 26],
      ],
      [
        [3, 19],
        [11, 27],
      ],
      [
        [4, 20],
        [12, 28],
      ],
      [
        [5, 21],
        [13, 29],
      ],
      [
        [6, 22],
        [14, 30],
      ],
      [
        [7, 23],
        [15, 31],
      ],
    ];
    test('accumulates values from deltas correctly from 0', () => {
      const biasDeltas = [
        [[0]],
        [[0]],
        [[0]],
        [[0]],
        [[0]],
        [[0]],
        [[0]],
        [[0]],
      ];
      const kernel = gpuMock(compareBiases, {
        output: [1, 1, 8],
        constants: {
          deltaWidth: 2,
          deltaHeight: 2,
        },
      });
      const result = kernel(biasDeltas, deltas);
      const expectedBiasDeltas = [
        [new Float32Array([48])],
        [new Float32Array([52])],
        [new Float32Array([56])],
        [new Float32Array([60])],
        [new Float32Array([64])],
        [new Float32Array([68])],
        [new Float32Array([72])],
        [new Float32Array([76])],
      ];

      expect(result).toEqual(expectedBiasDeltas);
    });
    test('accumulates values from deltas correctly from greater than 0', () => {
      const biasDeltas = [
        [[0]],
        [[1]],
        [[2]],
        [[3]],
        [[4]],
        [[5]],
        [[6]],
        [[7]],
      ];
      const kernel = gpuMock(compareBiases, {
        output: [1, 1, 8],
        constants: {
          deltaWidth: 2,
          deltaHeight: 2,
        },
      });
      const result = kernel(biasDeltas, deltas);
      const expectedBiasDeltas = [
        [new Float32Array([48])],
        [new Float32Array([53])],
        [new Float32Array([58])],
        [new Float32Array([63])],
        [new Float32Array([68])],
        [new Float32Array([73])],
        [new Float32Array([78])],
        [new Float32Array([83])],
      ];

      expect(result).toEqual(expectedBiasDeltas);
    });
  });
});


================================================
FILE: src/layer/convolution.ts
================================================
import { makeKernel, release, clone } from '../utilities/kernel';
import { getStride, getPadding } from '../utilities/layer-setup';
import { Filter } from './filter';
import { randos, randos3D } from '../utilities/randos';
import { zeros3D } from '../utilities/zeros-3d';
import { values } from '../utilities/values';
import {
  IConstantsThis,
  IKernelFunctionThis,
  IKernelRunShortcut,
  KernelOutput,
} from 'gpu.js';
import { ILayer, ILayerSettings } from './base-layer';
import { IPraxis } from '../praxis/base-praxis';

export interface IConvolutionConstantsBase extends IConstantsThis {
  paddingX: number;
  paddingY: number;
  strideX: number;
  strideY: number;
  filterWidth: number;
  filterHeight: number;
}

export interface IPredictConstants extends IConvolutionConstantsBase {
  inputWidth: number;
  inputHeight: number;
  inputDepth: number;
}

export function predict(
  this: IKernelFunctionThis<IPredictConstants>,
  inputs: number[][][],
  filters: number[][][],
  biases: number[]
): number {
  const startFilterX =
    this.constants.paddingX - this.thread.x * this.constants.strideX;
  const startInputX =
    this.thread.x * this.constants.strideX - this.constants.paddingX;
  const endFilterX = Math.min(
    this.constants.filterWidth,
    startFilterX + this.constants.inputWidth
  );

  const startFilterY =
    this.constants.paddingY - this.thread.y * this.constants.strideY;
  const startInputY =
    this.thread.y * this.constants.strideY - this.constants.paddingY;
  const endFilterY = Math.min(
    this.constants.filterHeight,
    startFilterY + this.constants.inputHeight
  );

  let sum = 0;
  for (let z = 0; z < this.constants.inputDepth; z++) {
    for (
      let filterY = Math.max(0, startFilterY),
        inputY = Math.max(0, startInputY);
      filterY < endFilterY;
      filterY++, inputY++
    ) {
      for (
        let filterX = Math.max(0, startFilterX),
          inputX = Math.max(0, startInputX);
        filterX < endFilterX;
        filterX++, inputX++
      ) {
        sum += filters[z][filterY][filterX] * inputs[z][inputY][inputX];
      }
    }
  }
  return sum + biases[this.thread.z];
}

export interface ICompareFilterDeltasConstants
  extends IConvolutionConstantsBase {
  deltaWidth: number;
  deltaHeight: number;
  inputWidth: number;
  inputHeight: number;
  deltaZ: number;
}

export function compareFilterDeltas(
  this: IKernelFunctionThis<ICompareFilterDeltasConstants>,
  filterDeltas: number[][][],
  inputs: number[][][],
  deltas: number[][][]
): number {
  const startDeltaX = Math.max(
    0,
    Math.ceil(
      (this.constants.paddingX - this.thread.x) / this.constants.strideX
    )
  );
  const startInputX =
    startDeltaX * this.constants.strideX +
    this.thread.x -
    this.constants.paddingX;
  const endDeltaX = Math.min(
    this.constants.deltaWidth,
    Math.floor(
      (this.constants.inputWidth -
        1 -
        this.thread.x +
        this.constants.paddingX) /
        this.constants.strideX
    ) + 1
  );

  const startDeltaY = Math.max(
    0,
    Math.ceil(
      (this.constants.paddingY - this.thread.y) / this.constants.strideY
    )
  );
  const startInputY =
    startDeltaY * this.constants.strideY +
    this.thread.y -
    this.constants.paddingY;
  const endDeltaY = Math.min(
    this.constants.deltaHeight,
    Math.floor(
      (this.constants.inputHeight -
        1 -
        this.thread.y +
        this.constants.paddingY) /
        this.constants.strideY
    ) + 1
  );

  let sum = filterDeltas[this.thread.z][this.thread.y][this.thread.x];
  for (
    let deltaY = startDeltaY, inputY = startInputY;
    deltaY < endDeltaY;
    deltaY++, inputY += this.constants.strideY
  ) {
    for (
      let deltaX = startDeltaX, inputX = startInputX;
      deltaX < endDeltaX;
      deltaX++, inputX += this.constants.strideX
    ) {
      sum +=
        inputs[this.thread.z][inputY][inputX] *
        deltas[this.constants.deltaZ][deltaY][deltaX];
    }
  }
  return sum;
}

export interface ICompareInputDeltasConstants
  extends IConvolutionConstantsBase {
  deltaHeight: number;
  deltaWidth: number;
  deltaZ: number;
}

export function compareInputDeltas(
  this: IKernelFunctionThis<ICompareInputDeltasConstants>,
  inputDeltas: number[][][],
  filters: number[][][],
  deltas: number[][][]
): number {
  const x = this.thread.x + this.constants.paddingX;
  const startDeltaX =
    x < this.constants.filterWidth
      ? 0
      : Math.floor(
          (x - this.constants.filterWidth + this.constants.strideX) /
            this.constants.strideX
        );
  const startFilterX = x - startDeltaX * this.constants.strideX;
  const endDeltaX = Math.min(
    startDeltaX + Math.floor(startFilterX / this.constants.strideX) + 1,
    this.constants.deltaWidth
  );

  const y = this.thread.y + this.constants.paddingY;
  const startDeltaY =
    y < this.constants.filterHeight
      ? 0
      : Math.floor(
          (y - this.constants.filterHeight + this.constants.strideY) /
            this.constants.strideY
        );
  const startFilterY = y - startDeltaY * this.constants.strideY;
  const endDeltaY = Math.min(
    startDeltaY + Math.floor(startFilterY / this.constants.strideY) + 1,
    this.constants.deltaHeight
  );

  let sum = inputDeltas[this.thread.z][this.thread.y][this.thread.x];
  let deltaY = startDeltaY;
  for (
    let filterY = startFilterY;
    deltaY < endDeltaY;
    filterY -= this.constants.strideY, deltaY++
  ) {
    let deltaX = startDeltaX;
    for (
      let filterX = startFilterX;
      deltaX < endDeltaX;
      filterX -= this.constants.strideX, deltaX++
    ) {
      sum +=
        filters[this.thread.z][filterY][filterX] *
        deltas[this.constants.deltaZ][deltaY][deltaX];
    }
  }
  return sum;
}

export interface ICompareBiasesConstants extends IConstantsThis {
  deltaHeight: number;
  deltaWidth: number;
}

export function compareBiases(
  this: IKernelFunctionThis<ICompareBiasesConstants>,
  biasDeltas: number[][][],
  deltas: number[][][]
): number {
  let sum = 0;
  for (let y = 0; y < this.constants.deltaHeight; y++) {
    for (let x = 0; x < this.constants.deltaWidth; x++) {
      sum += deltas[this.thread.z][y][x];
    }
  }
  return biasDeltas[this.thread.z][this.thread.y][this.thread.x] + sum;
}

export interface IConvolutionSettingsBase {
  stride?: number;
  strideX?: number;
  strideY?: number;
  padding?: number;
  paddingX?: number;
  paddingY?: number;
  filterCount?: number;
  filterWidth?: number;
  filterHeight?: number;
}

export interface IConvolutionSettings
  extends ILayerSettings,
    IConvolutionSettingsBase {
  bias?: number;
  biases?: KernelOutput;
  biasDeltas?: KernelOutput;
  filters?: KernelOutput;
  filterDeltas?: KernelOutput;
}

export const defaults: IConvolutionSettings = {
  stride: 0,
  padding: 0,
  bias: 0.1,
  filterCount: 1,
  filterWidth: 0,
  filterHeight: 0,
};

export class Convolution extends Filter {
  settings: Partial<IConvolutionSettings>;

  get strideX(): number {
    return this.settings.strideX as number;
  }

  get strideY(): number {
    return this.settings.strideY as number;
  }

  get paddingX(): number {
    return this.settings.paddingX as number;
  }

  get paddingY(): number {
    return this.settings.paddingX as number;
  }

  get width(): number {
    return Math.floor(
      (this.inputLayer.width + this.paddingX * 2 - this.filterWidth) /
        this.strideX +
        1
    );
  }

  get height(): number {
    return Math.floor(
      (this.inputLayer.height + this.paddingY * 2 - this.filterHeight) /
        this.strideY +
        1
    );
  }

  get bias(): number {
    return this.settings.bias as number;
  }

  get depth(): number {
    return this.filterCount;
  }

  get biases(): KernelOutput {
    return this.settings.biases;
  }

  set biases(biases: KernelOutput) {
    this.settings.biases = biases;
  }

  get biasDeltas(): KernelOutput {
    return this.settings.biasDeltas;
  }

  set biasDeltas(weights: KernelOutput) {
    this.settings.biasDeltas = weights;
  }

  get filters(): KernelOutput {
    return this.settings.filters;
  }

  set filters(filters: KernelOutput) {
    this.settings.filters = filters;
  }

  get filterDeltas(): KernelOutput {
    return this.settings.filterDeltas;
  }

  set filterDeltas(filterDeltas: KernelOutput) {
    this.settings.filterDeltas = filterDeltas;
  }

  constructor(settings: IConvolutionSettings, inputLayer: ILayer) {
    super(settings, inputLayer);
    this.settings = {
      ...defaults,
      ...settings,
      ...getPadding(settings, defaults),
      ...getStride(settings, defaults),
    };

    this.weights =
      settings.weights ?? randos3D(this.width, this.height, this.depth);
    this.deltas = zeros3D(this.width, this.height, this.depth);

    this.biases = values(this.depth, this.bias);
    this.biasDeltas = settings.biasDeltas ?? randos(this.depth);

    this.filters =
      settings.filters ??
      randos3D(this.filterWidth, this.filterHeight, this.filterCount);
    this.filterDeltas = zeros3D(
      this.filterWidth,
      this.filterHeight,
      this.filterCount
    );
    this.validate();
  }

  compareFilterDeltasKernel: IKernelRunShortcut | null = null;
  compareInputDeltasKernel: IKernelRunShortcut | null = null;
  compareBiasesKernel: IKernelRunShortcut | null = null;
  setupKernels(): void {
    this.predictKernel = makeKernel<
      Parameters<typeof predict>,
      IPredictConstants
    >(predict, {
      constants: {
        inputWidth: this.inputLayer.width,
        inputHeight: this.inputLayer.height,
        inputDepth: this.inputLayer.depth,
        strideX: this.strideX,
        strideY: this.strideY,
        paddingX: this.paddingX,
        paddingY: this.paddingY,
        filterWidth: this.filterWidth,
        filterHeight: this.filterHeight,
      },
      output: [this.width, this.height, this.depth],
      immutable: true,
    });

    this.compareFilterDeltasKernel = makeKernel(compareFilterDeltas, {
      constants: {
        deltaWidth: this.width,
        deltaHeight: this.height,
        deltaZ: this.depth,
        inputWidth: this.inputLayer.width,
        inputHeight: this.inputLayer.height,
        inputDepth: this.inputLayer.depth,
        strideX: this.strideX,
        strideY: this.strideY,
        paddingX: this.paddingX,
        paddingY: this.paddingY,
        filterWidth: this.filterWidth,
        filterHeight: this.filterHeight,
      },
      output: [this.width, this.height, this.depth],
      immutable: true,
    });

    this.compareInputDeltasKernel = makeKernel(compareInputDeltas, {
      constants: {
        deltaWidth: this.width,
        deltaHeight: this.height,
        deltaZ: this.depth,
        strideX: this.strideX,
        strideY: this.strideY,
        paddingX: this.paddingX,
        paddingY: this.paddingY,
        filterWidth: this.filterWidth,
        filterHeight: this.filterHeight,
        filterCount: this.filterCount,
      },
      output: [
        this.inputLayer.width,
        this.inputLayer.height,
        this.inputLayer.depth,
      ],
      immutable: true,
    });

    this.compareBiasesKernel = makeKernel(compareBiases, {
      output: [1, 1, this.depth],
      constants: {
        deltaWidth: this.width,
        deltaHeight: this.height,
      },
      immutable: true,
    });
  }

  predict(): void {
    this.weights = (this.predictKernel as IKernelRunShortcut)(
      this.inputLayer.weights,
      this.filters,
      this.biases
    );
  }

  compare(): void {
    const { filterDeltas, biasDeltas } = this;
    this.filterDeltas = (this.compareFilterDeltasKernel as IKernelRunShortcut)(
      filterDeltas,
      this.inputLayer.weights,
      this.deltas
    );
    release(filterDeltas);
    this.biasDeltas = (this.compareBiasesKernel as IKernelRunShortcut)(
      biasDeltas,
      this.deltas
    );
    release(biasDeltas);
    release(this.deltas);
    this.deltas = (this.compareInputDeltasKernel as IKernelRunShortcut)(
      this.filters,
      this.inputLayer.deltas
    );

    release(this.inputLayer.deltas);
    // TODO: do we need to clone here?
    this.inputLayer.deltas = clone(this.deltas);
  }

  learn(learningRate: number): void {
    // TODO: handle filters
    // TODO: do we need to release here?
    const { weights: oldWeights } = this;
    this.weights = (this.praxis as IPraxis).run(this, learningRate);
    release(oldWeights);
  }
}

export function convolution(
  settings: IConvolutionSettings,
  inputLayer: ILayer
): Convolution {
  return new Convolution(settings, inputLayer);
}


================================================
FILE: src/layer/dropout.test.ts
================================================
import { GPU } from 'gpu.js';
import { gpuMock } from 'gpu-mock.js';

import {
  dropout,
  Dropout,
  trainingPredict,
  predict,
  compare,
  setDropout,
  IDropoutSettings,
} from './dropout';
import {
  setup,
  teardown,
  makeKernel,
  makeKernelMap,
} from '../utilities/kernel';
import {
  IWithCompareKernel,
  IWithPredictKernelMap,
  mockLayer,
} from '../test-utils';

jest.mock('../utilities/kernel');

describe('Dropout Layer', () => {
  let validateMock: jest.SpyInstance;
  beforeEach(() => {
    setup(
      new GPU({
        mode: 'cpu',
      })
    );
    validateMock = jest.spyOn(Dropout.prototype, 'validate');
  });
  afterEach(() => {
    teardown();
    validateMock.mockRestore();
  });
  describe('dropout', () => {
    it('sends inputLayer and settings through and instantiates a Dropout', () => {
      const mockInputLayer = mockLayer({});
      const settings: IDropoutSettings = {
        probability: 100,
      };
      const layer = dropout(mockInputLayer, settings);
      expect(layer.constructor).toBe(Dropout);
      expect(layer.settings.probability).toBe(settings.probability);
    });
  });
  describe('.constructor', () => {
    it('sets inputLayer', () => {
      const mockInputLayer = mockLayer({});
      const layer = new Dropout(mockInputLayer);
      expect(layer.inputLayer).toBe(mockInputLayer);
    });
    it('sets height & width from inputLayer', () => {
      const mockInputLayer = mockLayer({
        width: 1,
        height: 2,
      });
      const layer = new Dropout(mockInputLayer);
      expect(layer.width).toBe(mockInputLayer.width);
      expect(layer.height).toBe(mockInputLayer.height);
    });
    it('sets probability from settings', () => {
      const mockInputLayer = mockLayer({});
      const settings: IDropoutSettings = {
        probability: 123,
      };
      const layer = new Dropout(mockInputLayer, settings);
      expect(layer.settings.probability).toBe(settings.probability);
    });
    it('calls this.validate', () => {
      const mockInputLayer = mockLayer({});
      const settings: IDropoutSettings = {
        probability: 123,
      };
      // eslint-disable-next-line no-new
      new Dropout(mockInputLayer, settings);
      expect(validateMock).toHaveBeenCalled();
    });
    it('sets dropouts to null', () => {
      const mockInputLayer = mockLayer({});
      const layer = new Dropout(mockInputLayer);
      expect(layer.dropouts).toBe(null);
    });
  });
  describe('trainingPredict (forward propagation)', () => {
    test('can dropout a simple matrix', () => {
      const inputs = [
        [1, 2, 3],
        [4, 5, 6],
        [7, 8, 9],
      ];

      const results = gpuMock(trainingPredict, {
        output: [3, 3],
        constants: {
          probability: 0.5,
        },
      })(inputs) as number[][];

      let hasZero = false;
      let hasNumber = false;

      for (let y = 0; y < results.length; y++) {
        const row = results[y];
        for (let x = 0; x < row.length; x++) {
          const value = row[x];
          if (value === 0) {
            hasZero = true;
          } else if (!Number.isNaN(value)) {
            hasNumber = true;
          }
        }
      }

      expect(hasZero).toBeTruthy();
      expect(hasNumber).toBeTruthy();
    });
  });
  describe('.training (forward propagation)', () => {
    test('can dropout a simple matrix', () => {
      const inputs = [
        [1, 2, 3],
        [4
Download .txt
gitextract_h9w2eaqg/

├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .gitattributes
├── .github/
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       └── nodejs.yml
├── .gitignore
├── .npmignore
├── .npmrc
├── .nvmrc
├── .prettierrc
├── .travis.yml
├── .vscode/
│   └── launch.json
├── LICENSE
├── README.md
├── jest.config.json
├── package.json
├── rollup.config.browser.js
├── rollup.config.js
├── src/
│   ├── README.md
│   ├── activation/
│   │   ├── README.md
│   │   ├── index.test.ts
│   │   ├── index.ts
│   │   ├── leaky-relu.test.ts
│   │   ├── leaky-relu.ts
│   │   ├── relu.test.ts
│   │   ├── relu.ts
│   │   ├── sigmoid.test.ts
│   │   ├── sigmoid.ts
│   │   ├── tanh.test.ts
│   │   └── tanh.ts
│   ├── autoencoder.test.ts
│   ├── autoencoder.ts
│   ├── cross-validate.test.ts
│   ├── cross-validate.ts
│   ├── errors/
│   │   └── untrained-neural-network-error.ts
│   ├── estimator/
│   │   └── mean-squared-error.ts
│   ├── feed-forward.end-to-end.test.ts
│   ├── feed-forward.ts
│   ├── feed-forward.unit.test.ts
│   ├── index.ts
│   ├── layer/
│   │   ├── README.md
│   │   ├── activation.test.ts
│   │   ├── activation.ts
│   │   ├── add.test.ts
│   │   ├── add.ts
│   │   ├── arthur-feed-forward.ts
│   │   ├── base-layer.test.ts
│   │   ├── base-layer.ts
│   │   ├── convolution.test.ts
│   │   ├── convolution.ts
│   │   ├── dropout.test.ts
│   │   ├── dropout.ts
│   │   ├── feed-forward.test.ts
│   │   ├── feed-forward.ts
│   │   ├── filter.ts
│   │   ├── fully-connected.test.ts
│   │   ├── fully-connected.ts
│   │   ├── gru.ts
│   │   ├── index.ts
│   │   ├── input.test.ts
│   │   ├── input.ts
│   │   ├── internal.ts
│   │   ├── leaky-relu.test.ts
│   │   ├── leaky-relu.ts
│   │   ├── lstm-cell.test.ts
│   │   ├── lstm-cell.ts
│   │   ├── modifier.ts
│   │   ├── multiply-element.test.ts
│   │   ├── multiply-element.ts
│   │   ├── multiply.test.ts
│   │   ├── multiply.ts
│   │   ├── negative.test.ts
│   │   ├── negative.ts
│   │   ├── ones.ts
│   │   ├── operator.ts
│   │   ├── output.ts
│   │   ├── pool.test.ts
│   │   ├── pool.ts
│   │   ├── random.test.ts
│   │   ├── random.ts
│   │   ├── recurrent-connection.ts
│   │   ├── recurrent-input.ts
│   │   ├── recurrent-zeros.ts
│   │   ├── regression.ts
│   │   ├── relu.test.ts
│   │   ├── relu.ts
│   │   ├── rnn-cell.test.ts
│   │   ├── rnn-cell.ts
│   │   ├── sigmoid.test.ts
│   │   ├── sigmoid.ts
│   │   ├── soft-max.test.ts
│   │   ├── soft-max.ts
│   │   ├── svm.ts
│   │   ├── tanh.test.ts
│   │   ├── tanh.ts
│   │   ├── target.test.ts
│   │   ├── target.ts
│   │   ├── transpose.ts
│   │   ├── types.ts
│   │   └── zeros.ts
│   ├── likely.test.ts
│   ├── likely.ts
│   ├── lookup.test.ts
│   ├── lookup.ts
│   ├── neural-network-gpu.end-to-end.test.ts
│   ├── neural-network-gpu.test.ts
│   ├── neural-network-gpu.ts
│   ├── neural-network-types.ts
│   ├── neural-network.bitwise.test.ts
│   ├── neural-network.json.test.ts
│   ├── neural-network.options.test.ts
│   ├── neural-network.test-method.test.ts
│   ├── neural-network.to-function.test.ts
│   ├── neural-network.trainopts.test.ts
│   ├── neural-network.ts
│   ├── neural-network.unit.test.ts
│   ├── praxis/
│   │   ├── README.md
│   │   ├── arthur-deviation-biases.end-to-end.test.ts
│   │   ├── arthur-deviation-biases.ts
│   │   ├── arthur-deviation-biases.unit.test.ts
│   │   ├── arthur-deviation-weights.end-to-end.test.ts
│   │   ├── arthur-deviation-weights.ts
│   │   ├── arthur-deviation-weights.unit.test.ts
│   │   ├── base-praxis.ts
│   │   ├── index.ts
│   │   ├── momentum-root-mean-squared-propagation.test.ts
│   │   └── momentum-root-mean-squared-propagation.ts
│   ├── recurrent/
│   │   ├── gru-time-step.test.ts
│   │   ├── gru-time-step.ts
│   │   ├── gru.test.ts
│   │   ├── gru.ts
│   │   ├── lstm-time-step.end-to-end.test.ts
│   │   ├── lstm-time-step.test.ts
│   │   ├── lstm-time-step.ts
│   │   ├── lstm.test.ts
│   │   ├── lstm.ts
│   │   ├── matrix/
│   │   │   ├── add-b.ts
│   │   │   ├── add.ts
│   │   │   ├── all-ones.ts
│   │   │   ├── clone-negative.ts
│   │   │   ├── clone.ts
│   │   │   ├── copy.ts
│   │   │   ├── equation.test.ts
│   │   │   ├── equation.ts
│   │   │   ├── index.test.ts
│   │   │   ├── index.ts
│   │   │   ├── max-i.ts
│   │   │   ├── multiply-b.ts
│   │   │   ├── multiply-element-b.ts
│   │   │   ├── multiply-element.ts
│   │   │   ├── multiply.ts
│   │   │   ├── ones-matrix.ts
│   │   │   ├── random-matrix.ts
│   │   │   ├── random-n-matrix.ts
│   │   │   ├── relu-b.ts
│   │   │   ├── relu.ts
│   │   │   ├── row-pluck-b.ts
│   │   │   ├── row-pluck.ts
│   │   │   ├── sample-i.ts
│   │   │   ├── sigmoid-b.ts
│   │   │   ├── sigmoid.ts
│   │   │   ├── softmax.ts
│   │   │   ├── tanh-b.ts
│   │   │   └── tanh.ts
│   │   ├── rnn-data-types.ts
│   │   ├── rnn-time-step.test.ts
│   │   ├── rnn-time-step.ts
│   │   ├── rnn.test.ts
│   │   └── rnn.ts
│   ├── recurrent.baseline.test.ts
│   ├── recurrent.end-to-end.test.ts
│   ├── recurrent.ts
│   ├── recurrent.unit.test.ts
│   ├── test-utils.ts
│   └── utilities/
│       ├── array-lookup-table.ts
│       ├── cast.test.ts
│       ├── cast.ts
│       ├── data-formatter.test.ts
│       ├── data-formatter.ts
│       ├── flatten-layers.test.ts
│       ├── flatten-layers.ts
│       ├── kernel.ts
│       ├── layer-from-json.test.ts
│       ├── layer-from-json.ts
│       ├── layer-setup.ts
│       ├── layer-size.test.ts
│       ├── layer-size.ts
│       ├── lookup-table.ts
│       ├── max.test.ts
│       ├── max.ts
│       ├── mse.test.ts
│       ├── mse.ts
│       ├── ones.test.ts
│       ├── ones.ts
│       ├── random-weight.test.ts
│       ├── random-weight.ts
│       ├── random.test.ts
│       ├── random.ts
│       ├── randos.test.ts
│       ├── randos.ts
│       ├── range.test.ts
│       ├── range.ts
│       ├── to-array.test.ts
│       ├── to-array.ts
│       ├── to-svg.test.ts
│       ├── to-svg.ts
│       ├── traverse-layers-excluding-from.ts
│       ├── traverse-layers-from.ts
│       ├── values-2d.ts
│       ├── values-3d.ts
│       ├── values.ts
│       ├── zeros-2d.ts
│       ├── zeros-3d.ts
│       ├── zeros.test.ts
│       └── zeros.ts
└── tsconfig.json
Download .txt
SYMBOL INDEX (1061 symbols across 133 files)

FILE: src/activation/leaky-relu.ts
  function activate (line 5) | function activate(weight: number): number {
  function measure (line 12) | function measure(weight: number, error: number): number {

FILE: src/activation/relu.ts
  function activate (line 5) | function activate(weight: number): number {
  function measure (line 12) | function measure(weight: number, delta: number): number {

FILE: src/activation/sigmoid.ts
  function activate (line 4) | function activate(value: number): number {
  function measure (line 11) | function measure(weight: number, error: number): number {

FILE: src/activation/tanh.ts
  function activate (line 4) | function activate(weight: number): number {
  function measure (line 11) | function measure(weight: number, error: number): number {

FILE: src/autoencoder.test.ts
  function xor (line 25) | function xor(...args: number[]) {
  function includesAnomalies (line 60) | function includesAnomalies(...args: number[]) {

FILE: src/autoencoder.ts
  type IAEOptions (line 15) | interface IAEOptions {
  class AE (line 24) | class AE<
    method constructor (line 31) | constructor(options?: Partial<IAEOptions>) {
    method denoise (line 57) | denoise(input: DecodedData): DecodedData {
    method decode (line 74) | decode(input: EncodedData): DecodedData {
    method encode (line 88) | encode(input: DecodedData): EncodedData {
    method likelyIncludesAnomalies (line 115) | likelyIncludesAnomalies(input: DecodedData, anomalyThreshold = 0.2): b...
    method train (line 148) | train(
    method createDecoder (line 173) | private createDecoder() {
    method encodedLayer (line 197) | private get encodedLayer(): KernelOutput {
    method encodedLayerIndex (line 204) | private get encodedLayerIndex(): number {

FILE: src/cross-validate.test.ts
  class FakeNN (line 11) | class FakeNN extends NeuralNetwork<number[], number[]> {
    method constructor (line 12) | constructor(
    method train (line 21) | train(data: Array<{ input: number[]; output: number[] }>) {
  class SpyFakeNN (line 37) | class SpyFakeNN extends FakeNN {
    method setActivation (line 38) | setActivation() {
    method setActivation (line 108) | setActivation() {
  class SpyFakeNN (line 107) | class SpyFakeNN extends FakeNN {
    method setActivation (line 38) | setActivation() {
    method setActivation (line 108) | setActivation() {
  type T (line 168) | interface T {
  class FakeNN (line 206) | class FakeNN extends NeuralNetwork<number[], number[]> {}
    method constructor (line 12) | constructor(
    method train (line 21) | train(data: Array<{ input: number[]; output: number[] }>) {
  class FakeNN (line 264) | class FakeNN extends NeuralNetwork<number[], number[]> {}
    method constructor (line 12) | constructor(
    method train (line 21) | train(data: Array<{ input: number[]; output: number[] }>) {

FILE: src/cross-validate.ts
  type InitClassifier (line 7) | type InitClassifier<
  type IClassifier (line 13) | interface IClassifier<TrainOptsType, JsonType, DatumType> {
  type ICrossValidateJSON (line 27) | type ICrossValidateJSON<JsonType> =
  type ICrossValidateStatsAverages (line 31) | interface ICrossValidateStatsAverages {
  type ICrossValidateStats (line 38) | interface ICrossValidateStats<JsonType> {
  type ICrossValidateBinaryStats (line 44) | interface ICrossValidateBinaryStats<NetworkType> {
  type ICrossValidateStatsResultStats (line 50) | interface ICrossValidateStatsResultStats {
  type ICrossValidateStatsResultBinaryStats (line 56) | interface ICrossValidateStatsResultBinaryStats
  type ICrossValidationTestPartitionResults (line 68) | interface ICrossValidationTestPartitionResults<JsonType>
  type ICrossValidationTestPartitionBinaryResults (line 77) | type ICrossValidationTestPartitionBinaryResults<
  class CrossValidate (line 82) | class CrossValidate<
    method constructor (line 107) | constructor(initClassifier: InitClassifierType) {
    method testPartition (line 111) | testPartition(
    method shuffleArray (line 148) | shuffleArray<K>(array: K[]): K[] {
    method train (line 179) | train(
    method toNeuralNetwork (line 286) | toNeuralNetwork(): ReturnType<InitClassifierType> {
    method toJSON (line 290) | toJSON(): ICrossValidateJSON<
    method fromJSON (line 296) | fromJSON(

FILE: src/errors/untrained-neural-network-error.ts
  class UntrainedNeuralNetworkError (line 1) | class UntrainedNeuralNetworkError<
    method constructor (line 4) | constructor(neuralNetwork: T) {

FILE: src/estimator/mean-squared-error.ts
  type mse2dThis (line 4) | interface mse2dThis extends IKernelFunctionThis {
  function mse2d (line 11) | function mse2d(
  class MeanSquaredError (line 24) | class MeanSquaredError {
    method constructor (line 34) | constructor({ width, height }: { width: number; height: number }) {

FILE: src/feed-forward.end-to-end.test.ts
  function setupTwinXORNetworks (line 36) | function setupTwinXORNetworks(useDecimals: boolean) {
  function testOutputsSmaller (line 311) | function testOutputsSmaller() {
  function testCanLearnXOR (line 335) | function testCanLearnXOR() {
  class SuperOutput (line 437) | class SuperOutput extends Target {
    method constructor (line 438) | constructor(settings: ILayerSettings, inputLayer: ILayer) {

FILE: src/feed-forward.ts
  type IFeedForwardTrainingData (line 14) | interface IFeedForwardTrainingData<
  type IFeedForwardNormalizedTrainingData (line 22) | interface IFeedForwardNormalizedTrainingData {
  type IFeedForwardGPUTrainingData (line 27) | interface IFeedForwardGPUTrainingData {
  type ITrainingStatus (line 32) | interface ITrainingStatus {
  type Log (line 37) | type Log = (status: string) => void;
  type FeedForwardCallback (line 38) | type FeedForwardCallback = (status: ITrainingStatus) => void;
  type IFeedForwardTrainingOptions (line 40) | interface IFeedForwardTrainingOptions {
  type IFeedForwardOptions (line 52) | interface IFeedForwardOptions {
  type IFeedForwardPreppedTrainingData (line 72) | interface IFeedForwardPreppedTrainingData {
  type IFeedForwardJSON (line 102) | interface IFeedForwardJSON {
  class FeedForward (line 110) | class FeedForward<
    method _validateTrainingOptions (line 114) | static _validateTrainingOptions(
    method _setLogMethod (line 161) | _setLogMethod(log: Log | undefined | boolean): void {
    method _updateTrainingOptions (line 172) | _updateTrainingOptions(opts: Partial<IFeedForwardTrainingOptions>): vo...
    method constructor (line 200) | constructor(options: IFeedForwardOptions = {}) {
    method _connectOptionsLayers (line 208) | _connectOptionsLayers(): ILayer[] {
    method _connectNewLayers (line 232) | _connectNewLayers(): ILayer[] {
    method _connectHiddenLayers (line 250) | _connectHiddenLayers(previousLayer: ILayer): ILayer[] {
    method initialize (line 267) | initialize(): void {
    method initializeLayers (line 275) | initializeLayers(layers: ILayer[]): void {
    method run (line 300) | run(input: InputType): OutputType {
    method runInput (line 330) | runInput(input: KernelOutput): KernelOutput {
    method train (line 339) | train(
    method trainAsync (line 359) | async trainAsync(
    method _trainingTick (line 391) | _trainingTick(
    method _prepTraining (line 434) | _prepTraining(
    method verifyIsInitialized (line 459) | verifyIsInitialized(): void {
    method _calculateTrainingError (line 465) | _calculateTrainingError(preparedData: IFeedForwardGPUTrainingData[]): ...
    method _trainPatterns (line 493) | _trainPatterns(data: IFeedForwardGPUTrainingData[]): void {
    method _trainPattern (line 499) | _trainPattern(
    method _calculateDeltas (line 522) | _calculateDeltas(target: KernelOutput): void {
    method adjustWeights (line 532) | adjustWeights(): void {
    method formatData (line 544) | formatData(
    method transferData (line 611) | transferData(
    method test (line 654) | test(): void {
    method toJSON (line 661) | toJSON(): IFeedForwardJSON {
    method fromJSON (line 709) | static fromJSON(
    method toFunction (line 790) | toFunction(): void {

FILE: src/feed-forward.unit.test.ts
  class TestLayer (line 242) | class TestLayer extends BaseLayer {
    method setupKernels (line 244) | setupKernels() {
    method setupKernels (line 274) | setupKernels() {
    method setupKernels (line 310) | setupKernels() {
    method setupKernels (line 350) | setupKernels() {}
    method predict (line 353) | predict() {
    method setupKernels (line 386) | setupKernels() {}
    method predict (line 389) | predict() {}
    method compare (line 392) | compare() {
    method setupKernels (line 425) | setupKernels() {}
    method predict (line 428) | predict() {}
    method compare (line 431) | compare() {}
    method learn (line 434) | learn() {
    method constructor (line 585) | constructor(settings: ILayerSettings, inputLayer?: ILayer) {
    method setupKernels (line 591) | setupKernels() {}
    method defaults (line 645) | static get defaults() {
    method constructor (line 650) | constructor(settings: ILayerSettings, inputLayer?: ILayer) {
    method setupKernels (line 656) | setupKernels() {}
  class TestLayer (line 272) | class TestLayer extends layerTypes.Model {
    method setupKernels (line 244) | setupKernels() {
    method setupKernels (line 274) | setupKernels() {
    method setupKernels (line 310) | setupKernels() {
    method setupKernels (line 350) | setupKernels() {}
    method predict (line 353) | predict() {
    method setupKernels (line 386) | setupKernels() {}
    method predict (line 389) | predict() {}
    method compare (line 392) | compare() {
    method setupKernels (line 425) | setupKernels() {}
    method predict (line 428) | predict() {}
    method compare (line 431) | compare() {}
    method learn (line 434) | learn() {
    method constructor (line 585) | constructor(settings: ILayerSettings, inputLayer?: ILayer) {
    method setupKernels (line 591) | setupKernels() {}
    method defaults (line 645) | static get defaults() {
    method constructor (line 650) | constructor(settings: ILayerSettings, inputLayer?: ILayer) {
    method setupKernels (line 656) | setupKernels() {}
  class TestLayer (line 308) | class TestLayer extends BaseLayer {
    method setupKernels (line 244) | setupKernels() {
    method setupKernels (line 274) | setupKernels() {
    method setupKernels (line 310) | setupKernels() {
    method setupKernels (line 350) | setupKernels() {}
    method predict (line 353) | predict() {
    method setupKernels (line 386) | setupKernels() {}
    method predict (line 389) | predict() {}
    method compare (line 392) | compare() {
    method setupKernels (line 425) | setupKernels() {}
    method predict (line 428) | predict() {}
    method compare (line 431) | compare() {}
    method learn (line 434) | learn() {
    method constructor (line 585) | constructor(settings: ILayerSettings, inputLayer?: ILayer) {
    method setupKernels (line 591) | setupKernels() {}
    method defaults (line 645) | static get defaults() {
    method constructor (line 650) | constructor(settings: ILayerSettings, inputLayer?: ILayer) {
    method setupKernels (line 656) | setupKernels() {}
  class TestLayer (line 348) | class TestLayer extends BaseLayer {
    method setupKernels (line 244) | setupKernels() {
    method setupKernels (line 274) | setupKernels() {
    method setupKernels (line 310) | setupKernels() {
    method setupKernels (line 350) | setupKernels() {}
    method predict (line 353) | predict() {
    method setupKernels (line 386) | setupKernels() {}
    method predict (line 389) | predict() {}
    method compare (line 392) | compare() {
    method setupKernels (line 425) | setupKernels() {}
    method predict (line 428) | predict() {}
    method compare (line 431) | compare() {}
    method learn (line 434) | learn() {
    method constructor (line 585) | constructor(settings: ILayerSettings, inputLayer?: ILayer) {
    method setupKernels (line 591) | setupKernels() {}
    method defaults (line 645) | static get defaults() {
    method constructor (line 650) | constructor(settings: ILayerSettings, inputLayer?: ILayer) {
    method setupKernels (line 656) | setupKernels() {}
  class TestLayer (line 384) | class TestLayer extends BaseLayer {
    method setupKernels (line 244) | setupKernels() {
    method setupKernels (line 274) | setupKernels() {
    method setupKernels (line 310) | setupKernels() {
    method setupKernels (line 350) | setupKernels() {}
    method predict (line 353) | predict() {
    method setupKernels (line 386) | setupKernels() {}
    method predict (line 389) | predict() {}
    method compare (line 392) | compare() {
    method setupKernels (line 425) | setupKernels() {}
    method predict (line 428) | predict() {}
    method compare (line 431) | compare() {}
    method learn (line 434) | learn() {
    method constructor (line 585) | constructor(settings: ILayerSettings, inputLayer?: ILayer) {
    method setupKernels (line 591) | setupKernels() {}
    method defaults (line 645) | static get defaults() {
    method constructor (line 650) | constructor(settings: ILayerSettings, inputLayer?: ILayer) {
    method setupKernels (line 656) | setupKernels() {}
  class TestLayer (line 423) | class TestLayer extends layerTypes.Model {
    method setupKernels (line 244) | setupKernels() {
    method setupKernels (line 274) | setupKernels() {
    method setupKernels (line 310) | setupKernels() {
    method setupKernels (line 350) | setupKernels() {}
    method predict (line 353) | predict() {
    method setupKernels (line 386) | setupKernels() {}
    method predict (line 389) | predict() {}
    method compare (line 392) | compare() {
    method setupKernels (line 425) | setupKernels() {}
    method predict (line 428) | predict() {}
    method compare (line 431) | compare() {}
    method learn (line 434) | learn() {
    method constructor (line 585) | constructor(settings: ILayerSettings, inputLayer?: ILayer) {
    method setupKernels (line 591) | setupKernels() {}
    method defaults (line 645) | static get defaults() {
    method constructor (line 650) | constructor(settings: ILayerSettings, inputLayer?: ILayer) {
    method setupKernels (line 656) | setupKernels() {}
  class TestInputLayer (line 465) | class TestInputLayer extends BaseLayer {
    method constructor (line 466) | constructor(settings: ILayerSettings) {
  class TestLayer1 (line 471) | class TestLayer1 extends BaseLayer {
    method constructor (line 473) | constructor(settings: ILayerSettings, inputLayer: ILayer) {
    method setupKernels (line 479) | setupKernels() {}
  class TestLayer2 (line 482) | class TestLayer2 extends BaseLayer {
    method constructor (line 484) | constructor(settings: ILayerSettings, inputLayer: ILayer) {
    method setupKernels (line 490) | setupKernels() {}
  class TestOperatorLayer (line 493) | class TestOperatorLayer extends BaseLayer {
    method constructor (line 496) | constructor(inputLayer1: ILayer, inputLayer2: ILayer) {
    method setupKernels (line 503) | setupKernels() {}
    method defaults (line 660) | static get defaults() {
    method constructor (line 666) | constructor(
    method setupKernels (line 677) | setupKernels() {}
  class TestOutputLayer (line 506) | class TestOutputLayer extends BaseLayer {
    method constructor (line 508) | constructor(settings: ILayerSettings, inputLayer: ILayer) {
  class TestLayer (line 583) | class TestLayer extends BaseLayer implements ILayer {
    method setupKernels (line 244) | setupKernels() {
    method setupKernels (line 274) | setupKernels() {
    method setupKernels (line 310) | setupKernels() {
    method setupKernels (line 350) | setupKernels() {}
    method predict (line 353) | predict() {
    method setupKernels (line 386) | setupKernels() {}
    method predict (line 389) | predict() {}
    method compare (line 392) | compare() {
    method setupKernels (line 425) | setupKernels() {}
    method predict (line 428) | predict() {}
    method compare (line 431) | compare() {}
    method learn (line 434) | learn() {
    method constructor (line 585) | constructor(settings: ILayerSettings, inputLayer?: ILayer) {
    method setupKernels (line 591) | setupKernels() {}
    method defaults (line 645) | static get defaults() {
    method constructor (line 650) | constructor(settings: ILayerSettings, inputLayer?: ILayer) {
    method setupKernels (line 656) | setupKernels() {}
  class TestLayer (line 644) | class TestLayer extends BaseLayer {
    method setupKernels (line 244) | setupKernels() {
    method setupKernels (line 274) | setupKernels() {
    method setupKernels (line 310) | setupKernels() {
    method setupKernels (line 350) | setupKernels() {}
    method predict (line 353) | predict() {
    method setupKernels (line 386) | setupKernels() {}
    method predict (line 389) | predict() {}
    method compare (line 392) | compare() {
    method setupKernels (line 425) | setupKernels() {}
    method predict (line 428) | predict() {}
    method compare (line 431) | compare() {}
    method learn (line 434) | learn() {
    method constructor (line 585) | constructor(settings: ILayerSettings, inputLayer?: ILayer) {
    method setupKernels (line 591) | setupKernels() {}
    method defaults (line 645) | static get defaults() {
    method constructor (line 650) | constructor(settings: ILayerSettings, inputLayer?: ILayer) {
    method setupKernels (line 656) | setupKernels() {}
  class TestOperatorLayer (line 659) | class TestOperatorLayer extends BaseLayer {
    method constructor (line 496) | constructor(inputLayer1: ILayer, inputLayer2: ILayer) {
    method setupKernels (line 503) | setupKernels() {}
    method defaults (line 660) | static get defaults() {
    method constructor (line 666) | constructor(
    method setupKernels (line 677) | setupKernels() {}

FILE: src/layer/activation.ts
  type ActivationType (line 5) | type ActivationType = new (
  class Activation (line 10) | class Activation extends BaseLayer {
    method width (line 13) | get width(): number {
    method height (line 17) | get height(): number {
    method depth (line 21) | get depth(): number {
    method constructor (line 25) | constructor(inputLayer: ILayer, settings?: Partial<ILayerSettings>) {

FILE: src/layer/add.ts
  function predict (line 7) | function predict(
  class Add (line 18) | class Add extends Operator {
    method width (line 19) | get width(): number {
    method height (line 23) | get height(): number {
    method depth (line 27) | get depth(): number {
    method validate (line 31) | validate(): void {
    method setupKernels (line 36) | setupKernels(): void {
    method predict (line 43) | predict(): void {
    method compare (line 51) | compare(): void {
  function add (line 60) | function add(

FILE: src/layer/arthur-feed-forward.ts
  type IArthurFeedForwardPraxisSettings (line 17) | interface IArthurFeedForwardPraxisSettings
  type IArthurFeedForwardSettings (line 21) | interface IArthurFeedForwardSettings extends IRandomSettings {
  function arthurFeedForward (line 28) | function arthurFeedForward(

FILE: src/layer/base-layer.test.ts
  type IPraxisExtendedSettings (line 29) | interface IPraxisExtendedSettings extends IPraxisSettings {

FILE: src/layer/base-layer.ts
  type ILayerJSON (line 12) | interface ILayerJSON {
  type ILayer (line 24) | interface ILayer {
  type ILayerSettings (line 48) | interface ILayerSettings {
  type BaseLayerType (line 74) | type BaseLayerType = new (settings?: Partial<ILayerSettings>) => ILayer;
  class BaseLayer (line 76) | class BaseLayer implements ILayer {
    method width (line 82) | get width(): number {
    method height (line 86) | get height(): number {
    method depth (line 90) | get depth(): number {
    method weights (line 94) | get weights(): KernelOutput | Input {
    method weights (line 98) | set weights(weights: KernelOutput | Input) {
    method deltas (line 105) | get deltas(): KernelOutput {
    method deltas (line 109) | set deltas(deltas: KernelOutput) {
    method id (line 113) | get id(): string {
    method id (line 117) | set id(title: string) {
    method constructor (line 121) | constructor(settings?: Partial<ILayerSettings>) {
    method setupPraxis (line 130) | setupPraxis(): void {
    method validate (line 196) | validate(): void {
    method setupKernels (line 211) | setupKernels(isTraining?: boolean): void {}
    method reuseKernels (line 213) | reuseKernels(layer: ILayer): void {
    method predict (line 243) | predict(inputs?: KernelOutput): void {}
    method compare (line 245) | compare(targetValues?: KernelOutput): void {}
    method learn (line 247) | learn(learningRate?: number): void {}
    method toArray (line 249) | toArray(): TextureArrayOutput {
    method toJSON (line 255) | toJSON(): Partial<ILayerJSON> {
    method toJSON (line 259) | static toJSON(layer: ILayer): Partial<ILayerJSON> {
  function toUntypedArray (line 283) | function toUntypedArray(

FILE: src/layer/convolution.ts
  type IConvolutionConstantsBase (line 16) | interface IConvolutionConstantsBase extends IConstantsThis {
  type IPredictConstants (line 25) | interface IPredictConstants extends IConvolutionConstantsBase {
  function predict (line 31) | function predict(
  type ICompareFilterDeltasConstants (line 76) | interface ICompareFilterDeltasConstants
  function compareFilterDeltas (line 85) | function compareFilterDeltas(
  type ICompareInputDeltasConstants (line 152) | interface ICompareInputDeltasConstants
  function compareInputDeltas (line 159) | function compareInputDeltas(
  type ICompareBiasesConstants (line 214) | interface ICompareBiasesConstants extends IConstantsThis {
  function compareBiases (line 219) | function compareBiases(
  type IConvolutionSettingsBase (line 233) | interface IConvolutionSettingsBase {
  type IConvolutionSettings (line 245) | interface IConvolutionSettings
  class Convolution (line 264) | class Convolution extends Filter {
    method strideX (line 267) | get strideX(): number {
    method strideY (line 271) | get strideY(): number {
    method paddingX (line 275) | get paddingX(): number {
    method paddingY (line 279) | get paddingY(): number {
    method width (line 283) | get width(): number {
    method height (line 291) | get height(): number {
    method bias (line 299) | get bias(): number {
    method depth (line 303) | get depth(): number {
    method biases (line 307) | get biases(): KernelOutput {
    method biases (line 311) | set biases(biases: KernelOutput) {
    method biasDeltas (line 315) | get biasDeltas(): KernelOutput {
    method biasDeltas (line 319) | set biasDeltas(weights: KernelOutput) {
    method filters (line 323) | get filters(): KernelOutput {
    method filters (line 327) | set filters(filters: KernelOutput) {
    method filterDeltas (line 331) | get filterDeltas(): KernelOutput {
    method filterDeltas (line 335) | set filterDeltas(filterDeltas: KernelOutput) {
    method constructor (line 339) | constructor(settings: IConvolutionSettings, inputLayer: ILayer) {
    method setupKernels (line 369) | setupKernels(): void {
    method predict (line 439) | predict(): void {
    method compare (line 447) | compare(): void {
    method learn (line 471) | learn(learningRate: number): void {
  function convolution (line 480) | function convolution(

FILE: src/layer/dropout.ts
  function setDropout (line 13) | function setDropout(dropout: number): number {
  type IDropoutConstants (line 17) | interface IDropoutConstants extends IConstantsThis {
  function trainingPredict (line 21) | function trainingPredict(
  function predict (line 31) | function predict(
  function compare (line 38) | function compare(
  type IDropoutSettings (line 49) | interface IDropoutSettings extends ILayerSettings {
  class Dropout (line 58) | class Dropout extends Filter {
    method constructor (line 62) | constructor(
    method setupKernels (line 72) | setupKernels(isTraining?: boolean): void {
    method predict (line 92) | predict(): void {
    method compare (line 105) | compare(): void {
  function dropout (line 114) | function dropout(

FILE: src/layer/feed-forward.ts
  function feedForward (line 7) | function feedForward(settings: ILayerSettings, input: ILayer): ILayer {

FILE: src/layer/filter.ts
  type IFilterSettings (line 4) | interface IFilterSettings extends ILayerSettings {
  type FilterType (line 12) | type FilterType = new (
  class Filter (line 17) | class Filter extends BaseLayer {
    method width (line 18) | get width(): number {
    method height (line 22) | get height(): number {
    method depth (line 26) | get depth(): number {
    method filterCount (line 30) | get filterCount(): number {
    method filterWidth (line 34) | get filterWidth(): number {
    method filterHeight (line 38) | get filterHeight(): number {
    method filters (line 42) | get filters(): KernelOutput {
    method filters (line 46) | set filters(filters: KernelOutput) {
    method filterDeltas (line 50) | get filterDeltas(): KernelOutput {
    method filterDeltas (line 54) | set filterDeltas(filterDeltas: KernelOutput) {
    method constructor (line 60) | constructor(settings: Partial<IFilterSettings>, inputLayer: ILayer) {

FILE: src/layer/fully-connected.ts
  type IPredictConstants (line 16) | interface IPredictConstants extends IConstantsThis {
  function predict (line 21) | function predict(
  function predict3D (line 38) | function predict3D(
  type ICompareInputDeltasConstants (line 57) | interface ICompareInputDeltasConstants extends IConstantsThis {
  function compareInputDeltas (line 61) | function compareInputDeltas(
  function compareInputDeltas3D (line 75) | function compareInputDeltas3D(
  function compareBiases (line 89) | function compareBiases(
  type ICompareFiltersDeltas (line 97) | interface ICompareFiltersDeltas extends IConstantsThis {
  function compareFilterDeltas (line 104) | function compareFilterDeltas(
  function compareFilterDeltas3D (line 117) | function compareFilterDeltas3D(
  type IFullyConnectedDefaultSettings (line 140) | interface IFullyConnectedDefaultSettings
  class FullyConnected (line 151) | class FullyConnected extends Filter {
    method bias (line 152) | get bias(): number {
    method biases (line 156) | get biases(): KernelOutput {
    method biases (line 160) | set biases(biases: KernelOutput) {
    method biasDeltas (line 164) | get biasDeltas(): KernelOutput {
    method biasDeltas (line 168) | set biasDeltas(biasDeltas: KernelOutput) {
    method constructor (line 176) | constructor(
    method validate (line 202) | validate(): void {
    method setupKernels (line 207) | setupKernels(): void {
    method predict (line 271) | predict(): void {
    method compare (line 279) | compare(): void {
  function fullyConnected (line 307) | function fullyConnected(

FILE: src/layer/gru.ts
  function gru (line 13) | function gru(

FILE: src/layer/input.ts
  class Input (line 11) | class Input extends EntryPoint {
    method constructor (line 13) | constructor(settings: ILayerSettings) {
    method setupKernels (line 20) | setupKernels(): void {
    method reuseKernels (line 35) | reuseKernels(layer: ILayer): void {
    method predict (line 40) | predict(inputs: KernelOutput): void {
    method predict1D (line 60) | predict1D(inputs: KernelOutput): void {
    method compare (line 69) | compare(): void {
    method learn (line 73) | learn(): void {}
  function input (line 76) | function input(settings: ILayerSettings): Input {

FILE: src/layer/internal.ts
  type InternalType (line 5) | type InternalType = new (settings: Partial<ILayerSettings>) => ILayer;
  method width (line 17) | get width(): number {
  method height (line 21) | get height(): number {
  method depth (line 25) | get depth(): number {
  method weights (line 29) | get weights(): KernelOutput | Input {
  method weights (line 33) | set weights(weights: KernelOutput | Input) {
  method deltas (line 37) | get deltas(): KernelOutput {
  method deltas (line 41) | set deltas(deltas: KernelOutput) {
  method toJSON (line 45) | toJSON(): Partial<ILayerJSON> {

FILE: src/layer/leaky-relu.ts
  function predict2D (line 7) | function predict2D(
  function predict3D (line 14) | function predict3D(
  function compare2D (line 21) | function compare2D(
  function compare3D (line 32) | function compare3D(
  class LeakyRelu (line 43) | class LeakyRelu extends Activation {
    method setupKernels (line 44) | setupKernels(): void {
    method predict (line 73) | predict(): void {
    method compare (line 80) | compare(): void {
  function leakyRelu (line 90) | function leakyRelu(

FILE: src/layer/lstm-cell.ts
  function lstmCell (line 11) | function lstmCell(

FILE: src/layer/modifier.ts
  type ModifierType (line 3) | type ModifierType = new (
  class Modifier (line 8) | class Modifier extends BaseLayer {
    method constructor (line 10) | constructor(inputLayer: ILayer, settings?: Partial<ILayerSettings>) {
    method validate (line 20) | validate(): void {

FILE: src/layer/multiply-element.ts
  function predict (line 7) | function predict(
  function compare (line 18) | function compare(
  class MultiplyElement (line 28) | class MultiplyElement extends Operator {
    method width (line 29) | get width(): number {
    method height (line 33) | get height(): number {
    method depth (line 37) | get depth(): number {
    method validate (line 41) | validate(): void {
    method setupKernels (line 46) | setupKernels(): void {
    method predict (line 58) | predict(): void {
    method compare (line 66) | compare(): void {
  function multiplyElement (line 80) | function multiplyElement(

FILE: src/layer/multiply.ts
  type IMultiplyConstants (line 11) | interface IMultiplyConstants extends IConstantsThis {
  function predict (line 15) | function predict(
  function compareFromX (line 27) | function compareFromX(
  function compareFromY (line 40) | function compareFromY(
  class Multiply (line 53) | class Multiply extends Operator {
    method width (line 57) | get width(): number {
    method width (line 61) | set width(width: number) {
    method height (line 65) | get height(): number {
    method height (line 69) | set height(height: number) {
    method depth (line 73) | get depth(): number {
    method depth (line 77) | set depth(depth: number) {
    method validate (line 81) | validate(): void {
    method setupKernels (line 90) | setupKernels(): void {
    method reuseKernels (line 114) | reuseKernels(layer: ILayer): void {
    method predict (line 120) | predict(): void {
    method compare (line 129) | compare(): void {
    method setupPraxis (line 154) | setupPraxis(): void {}
    method toJSON (line 156) | toJSON(): Partial<ILayerJSON> {
  function multiply (line 165) | function multiply(

FILE: src/layer/negative.ts
  function predict (line 6) | function predict(
  class Negative (line 13) | class Negative extends Modifier {
    method constructor (line 14) | constructor(inputLayer: ILayer, settings?: ILayerSettings) {
    method setupKernels (line 19) | setupKernels(): void {
    method predict (line 25) | predict(): void {
  function negative (line 32) | function negative(

FILE: src/layer/ones.ts
  class Ones (line 7) | class Ones extends Model {
    method constructor (line 8) | constructor(settings: ILayerSettings) {
  function ones (line 16) | function ones(settings: ILayerSettings): Ones {

FILE: src/layer/operator.ts
  type OperatorType (line 4) | type OperatorType = new (
  method constructor (line 13) | constructor(

FILE: src/layer/output.ts
  function output (line 7) | function output(settings: ILayerSettings, inputLayer: ILayer): ILayer {

FILE: src/layer/pool.ts
  function setSwitchY (line 19) | function setSwitchY(value: number): number {
  function setSwitchX (line 23) | function setSwitchX(value: number): number {
  type IPredictConstants (line 27) | interface IPredictConstants extends IConvolutionConstantsBase {
  function predict (line 32) | function predict(
  type ICompareConstants (line 67) | interface ICompareConstants extends IConvolutionConstantsBase {
  function compare (line 75) | function compare(
  function compare3D (line 126) | function compare3D(
  type IPoolSettings (line 179) | interface IPoolSettings
  class Pool (line 194) | class Pool extends Filter {
    method strideX (line 197) | get strideX(): number {
    method strideY (line 201) | get strideY(): number {
    method paddingX (line 205) | get paddingX(): number {
    method paddingY (line 209) | get paddingY(): number {
    method width (line 213) | get width(): number {
    method height (line 223) | get height(): number {
    method depth (line 233) | get depth(): number {
    method filterCount (line 237) | get filterCount(): number {
    method switchX (line 242) | get switchX(): KernelOutput {
    method switchX (line 246) | set switchX(switchX: KernelOutput) {
    method switchY (line 250) | get switchY(): KernelOutput {
    method switchY (line 254) | set switchY(switchY: KernelOutput) {
    method constructor (line 259) | constructor(settings: IPoolSettings, inputLayer: ILayer) {
    method setupKernels (line 272) | setupKernels(): void {
    method predict (line 315) | predict(): void {
    method compare (line 325) | compare(): void {
  function pool (line 346) | function pool(settings: IPoolSettings, inputLayer: ILayer): Pool {

FILE: src/layer/random.ts
  type IRandomSettings (line 6) | interface IRandomSettings extends ILayerSettings {
  class Random (line 15) | class Random extends Model implements ILayer {
    method constructor (line 17) | constructor(settings: Partial<IRandomSettings>) {
    method predict (line 31) | predict(): void {}
    method compare (line 33) | compare(): void {}
  function random (line 36) | function random(settings: IRandomSettings): Random {

FILE: src/layer/recurrent-connection.ts
  class RecurrentConnection (line 7) | class RecurrentConnection extends Internal {
    method setLayer (line 10) | setLayer(layer: ILayer): void {
    method width (line 14) | get width(): number {
    method width (line 19) | set width(value: number) {
    method height (line 23) | get height(): number {
    method height (line 28) | set height(value: number) {
    method deltas (line 32) | get deltas(): KernelOutput {
    method deltas (line 37) | set deltas(deltas: KernelOutput) {
    method weights (line 43) | get weights(): KernelOutput {
    method weights (line 48) | set weights(weights: KernelOutput) {
    method predict (line 54) | predict(): void {
    method compare (line 58) | compare(): void {
    method learn (line 62) | learn(): void {
    method setupKernels (line 66) | setupKernels(): void {
    method reuseKernels (line 72) | reuseKernels(): void {

FILE: src/layer/recurrent-input.ts
  type IRecurrentInput (line 7) | interface IRecurrentInput extends ILayer {
  class RecurrentInput (line 11) | class RecurrentInput extends Internal implements IRecurrentInput {
    method constructor (line 17) | constructor(recurrentInput: ILayer) {
    method width (line 23) | get width(): number {
    method height (line 27) | get height(): number {
    method depth (line 31) | get depth(): number {
    method deltas (line 35) | get deltas(): KernelOutput {
    method deltas (line 39) | set deltas(deltas: KernelOutput) {
    method weights (line 45) | get weights(): KernelOutput {
    method weights (line 49) | set weights(weights: KernelOutput) {
    method validate (line 55) | validate(): void {
    method setDimensions (line 70) | setDimensions(width: number, height: number): void {
    method predict (line 75) | predict(): void {
    method compare (line 79) | compare(): void {
    method learn (line 83) | learn(): void {
    method setupKernels (line 87) | setupKernels(): void {
    method reuseKernels (line 93) | reuseKernels(): void {

FILE: src/layer/recurrent-zeros.ts
  class RecurrentZeros (line 8) | class RecurrentZeros extends Internal implements IRecurrentInput {
    method constructor (line 14) | constructor(settings?: Partial<ILayerSettings>) {
    method setDimensions (line 21) | setDimensions(width: number, height: number): void {
    method setupKernels (line 32) | setupKernels(): void {
    method reuseKernels (line 38) | reuseKernels(): void {
    method predict (line 44) | predict(): void {
    method compare (line 48) | compare(): void {
    method learn (line 52) | learn(learningRate: number): void {
  function recurrentZeros (line 68) | function recurrentZeros(): RecurrentZeros {

FILE: src/layer/regression.ts
  class Regression (line 6) | class Regression extends BaseLayer {
    method constructor (line 8) | constructor(settings: ILayerSettings, inputLayer: ILayer) {
    method predict (line 14) | predict(): void {
    method learn (line 19) | learn(): void {
  function learn (line 25) | function learn(
  function regression (line 34) | function regression(

FILE: src/layer/relu.ts
  function predict2D (line 8) | function predict2D(
  function compare2D (line 15) | function compare2D(
  function predict3D (line 26) | function predict3D(
  function compare3D (line 33) | function compare3D(
  class Relu (line 44) | class Relu extends Activation {
    method setupKernels (line 45) | setupKernels(): void {
    method predict (line 74) | predict(): void {
    method compare (line 81) | compare(): void {
  function relu (line 90) | function relu(inputLayer: ILayer, settings?: ILayerSettings): Relu {

FILE: src/layer/rnn-cell.ts
  function rnnCell (line 9) | function rnnCell(

FILE: src/layer/sigmoid.ts
  function predict2D (line 8) | function predict2D(
  function predict3D (line 15) | function predict3D(
  function compare2D (line 24) | function compare2D(
  function compare3D (line 34) | function compare3D(
  class Sigmoid (line 44) | class Sigmoid extends Activation {
    method setupKernels (line 45) | setupKernels(): void {
    method predict (line 73) | predict(): void {
    method compare (line 80) | compare(): void {
    method learn (line 88) | learn(learningRate?: number): void {}
  function sigmoid (line 91) | function sigmoid(

FILE: src/layer/soft-max.ts
  type ISoftMaxConstants (line 17) | interface ISoftMaxConstants extends IConstantsThis {
  function getMaxValue (line 21) | function getMaxValue(
  function getMaxValue2D (line 35) | function getMaxValue2D(
  function getMaxValue3D (line 51) | function getMaxValue3D(
  function getSum (line 69) | function getSum(
  function getSum2D (line 80) | function getSum2D(
  function getSum3D (line 93) | function getSum3D(
  function getExponentials (line 108) | function getExponentials(
  function getExponentials2D (line 116) | function getExponentials2D(
  function getExponentials3D (line 124) | function getExponentials3D(
  function predict (line 134) | function predict(
  function predict2D (line 142) | function predict2D(
  function predict3D (line 150) | function predict3D(
  function compare (line 161) | function compare(
  function compare2D (line 173) | function compare2D(
  function compare3D (line 186) | function compare3D(
  function loss (line 204) | function loss(): number {
  class SoftMax (line 210) | class SoftMax extends Modifier {
    method constructor (line 215) | constructor(inputLayer: ILayer, settings?: ILayerSettings) {
    method setupKernels (line 234) | setupKernels(): void {
    method predict (line 291) | predict(): void {
    method compare (line 308) | compare(targetValues: KernelOutput): void {
  function softMax (line 324) | function softMax(

FILE: src/layer/svm.ts
  class SVM (line 6) | class SVM extends BaseLayer {
    method constructor (line 8) | constructor(inputLayer: ILayer, settings: ILayerSettings) {
    method predict (line 13) | predict(): void {
    method learn (line 19) | learn(): void {
  function svm (line 37) | function svm(inputLayer: ILayer, settings: ILayerSettings): SVM {

FILE: src/layer/tanh.ts
  function predict2D (line 8) | function predict2D(
  function predict3D (line 15) | function predict3D(
  function compare2D (line 22) | function compare2D(
  function compare3D (line 33) | function compare3D(
  class Tanh (line 44) | class Tanh extends Activation {
    method setupKernels (line 45) | setupKernels(): void {
    method predict (line 73) | predict(): void {
    method compare (line 80) | compare(): void {
  function tanh (line 89) | function tanh(inputLayer: ILayer, settings?: ILayerSettings): Tanh {

FILE: src/layer/target.ts
  function compare1D (line 8) | function compare1D(
  function compare2D (line 16) | function compare2D(
  type TargetType (line 27) | type TargetType = new (
  class Target (line 32) | class Target extends BaseLayer {
    method constructor (line 35) | constructor(settings: Partial<ILayerSettings>, inputLayer: ILayer) {
    method setupKernels (line 52) | setupKernels(): void {
    method predict (line 66) | predict(): void {
    method compare (line 73) | compare(targetValues: KernelOutput): void {
    method setupPraxis (line 87) | setupPraxis(): void {}
  function target (line 90) | function target(settings: ILayerSettings, inputLayer: ILayer): Target {

FILE: src/layer/transpose.ts
  function predict (line 6) | function predict(this: IKernelFunctionThis, value: number[][]): number {
  class Transpose (line 12) | class Transpose extends Modifier {
    method width (line 13) | get width(): number {
    method height (line 17) | get height(): number {
    method constructor (line 21) | constructor(inputLayer: ILayer) {
    method setupKernels (line 26) | setupKernels(): void {
    method predict (line 35) | predict(): void {
    method compare (line 41) | compare(): void {
  function transpose (line 48) | function transpose(inputLayer: ILayer): Transpose {

FILE: src/layer/types.ts
  class InternalModel (line 11) | class InternalModel {}
  type EntryPointType (line 13) | type EntryPointType = new (settings: Partial<ILayerSettings>) => ILayer;
  class EntryPoint (line 15) | class EntryPoint extends BaseLayer {}
  class Model (line 18) | class Model extends BaseLayer {
    method learn (line 19) | learn(learningRate?: number): void {

FILE: src/layer/zeros.ts
  class Zeros (line 5) | class Zeros extends Model {
    method constructor (line 6) | constructor(settings: ILayerSettings) {
    method predict (line 13) | predict(): void {
    method compare (line 17) | compare(): void {
  function zeros (line 24) | function zeros(settings: ILayerSettings): Zeros {

FILE: src/likely.test.ts
  function integer (line 7) | function integer(character: string): number {
  function character (line 15) | function character(string: string): number[] {

FILE: src/likely.ts
  type ILikelyNet (line 1) | interface ILikelyNet<InputType, OutputType> {
  function likely (line 5) | function likely<

FILE: src/lookup.ts
  type INumberHash (line 3) | interface INumberHash {
  type INumberArray (line 7) | interface INumberArray {
  type InputOutputValue (line 13) | type InputOutputValue = INumberArray | Partial<INumberHash>;
  type ITrainingDatum (line 15) | interface ITrainingDatum {
  type FormattableData (line 20) | type FormattableData =
  method toTable (line 33) | toTable(hashes: INumberHash[]): INumberHash {
  method toTable2D (line 44) | toTable2D(objects2D: INumberHash[][]): INumberHash {
  method toInputTable2D (line 61) | toInputTable2D(
  method toOutputTable2D (line 81) | toOutputTable2D(
  method toHash (line 104) | toHash(hash: INumberHash): INumberHash {
  method toArray (line 117) | toArray(
  method toArrayShort (line 130) | toArrayShort(lookup: INumberHash, object: INumberHash): Float32Array {
  method toArrays (line 140) | toArrays(
  method toObject (line 158) | toObject(lookup: INumberHash, array: number[] | Float32Array): INumberHa...
  method toObjectPartial (line 167) | toObjectPartial(
  method dataShape (line 188) | dataShape(data: FormattableData[] | FormattableData): string[] {
  method addKeys (line 255) | addKeys(value: number[] | INumberHash, table: INumberHash): INumberHash {

FILE: src/neural-network-gpu.ts
  type INeuralNetworkGPUDatumFormatted (line 26) | interface INeuralNetworkGPUDatumFormatted {
  type INeuralNetworkGPUPreppedTrainingData (line 31) | interface INeuralNetworkGPUPreppedTrainingData
  type ISizedKernelThis (line 37) | interface ISizedKernelThis extends IKernelFunctionThis {
  function weightedSumSigmoid (line 43) | function weightedSumSigmoid(
  function weightedSumRelu (line 57) | function weightedSumRelu(
  function weightedSumLeakyRelu (line 71) | function weightedSumLeakyRelu(
  function weightedSumTanh (line 85) | function weightedSumTanh(
  function calcErrorOutput (line 99) | function calcErrorOutput(output: number, target: number): number {
  function calcDeltasSigmoid (line 103) | function calcDeltasSigmoid(error: number, output: number): number {
  function calcDeltasRelu (line 108) | function calcDeltasRelu(error: number, output: number): number {
  function calcDeltasLeakyRelu (line 113) | function calcDeltasLeakyRelu(error: number, output: number): number {
  function calcDeltasTanh (line 118) | function calcDeltasTanh(error: number, output: number): number {
  function calcError (line 123) | function calcError(
  type ILearningKernelThis (line 136) | interface ILearningKernelThis extends IKernelFunctionThis {
  function calcChanges (line 143) | function calcChanges(
  function addWeights (line 153) | function addWeights(change: number, weight: number): number {
  function addBiases (line 157) | function addBiases(
  function mse (line 168) | function mse(this: ISizedKernelThis, errors: number[]): number {
  type INeuralNetworkGPUOptions (line 176) | interface INeuralNetworkGPUOptions extends INeuralNetworkOptions {
  type BackPropagateOutput (line 180) | type BackPropagateOutput = (
  type BackPropagateLayer (line 186) | type BackPropagateLayer = (
  class NeuralNetworkGPU (line 193) | class NeuralNetworkGPU<
    method constructor (line 263) | constructor(options: Partial<INeuralNetworkGPUOptions> = {}) {
    method initialize (line 269) | initialize(): void {
    method setActivation (line 278) | setActivation(): void {}
    method trainPattern (line 282) | trainPattern(
    method calculateTrainingError (line 299) | calculateTrainingError(data: INeuralNetworkGPUDatumFormatted[]): number {
    method adjustWeights (line 315) | adjustWeights(): void {
    method buildRunInput (line 320) | buildRunInput(): void {
    method buildCalculateDeltas (line 382) | buildCalculateDeltas(): void {
    method buildGetChanges (line 496) | buildGetChanges(): void {
    method getChanges (line 539) | getChanges(): void {
    method buildChangeBiases (line 557) | buildChangeBiases(): void {
    method changeBiases (line 570) | changeBiases(): void {
    method buildGetMSE (line 581) | buildGetMSE(): void {
    method run (line 614) | run(input: InputType): OutputType {
    method prepTraining (line 646) | prepTraining(
    method toFunction (line 683) | toFunction(): (input: InputType) => OutputType {
    method toJSON (line 689) | toJSON(): INeuralNetworkJSON {

FILE: src/neural-network-types.ts
  type INeuralNetworkOptions (line 2) | interface INeuralNetworkOptions {
  type INeuralNetworkTrainingOptions (line 16) | interface INeuralNetworkTrainingOptions {
  type INeuralNetworkTrainingCallback (line 72) | type INeuralNetworkTrainingCallback = (
  type INeuralNetworkState (line 76) | interface INeuralNetworkState {
  type INeuralNetworkTestResult (line 81) | interface INeuralNetworkTestResult {
  type INeuralNetworkBinaryTestResult (line 87) | interface INeuralNetworkBinaryTestResult

FILE: src/neural-network.bitwise.test.ts
  function testBitwise (line 5) | function testBitwise(
  function testBitwiseAdam (line 60) | function testBitwiseAdam(
  function testBitwiseAsync (line 119) | async function testBitwiseAsync(

FILE: src/neural-network.to-function.test.ts
  type IInput (line 28) | interface IInput extends INumberHash {
  type IOutput (line 34) | interface IOutput extends INumberHash {

FILE: src/neural-network.ts
  type NeuralNetworkFormatter (line 16) | type NeuralNetworkFormatter =
  function getTypedArrayFn (line 20) | function getTypedArrayFn(
  type NeuralNetworkActivation (line 43) | type NeuralNetworkActivation =
  type IJSONLayer (line 49) | interface IJSONLayer {
  type INeuralNetworkJSON (line 54) | interface INeuralNetworkJSON {
  type INeuralNetworkOptions (line 66) | interface INeuralNetworkOptions {
  function defaults (line 73) | function defaults(): INeuralNetworkOptions {
  type INeuralNetworkTrainOptionsJSON (line 81) | interface INeuralNetworkTrainOptionsJSON {
  type INeuralNetworkPreppedTrainingData (line 98) | interface INeuralNetworkPreppedTrainingData<T> {
  type INeuralNetworkTrainOptions (line 104) | interface INeuralNetworkTrainOptions {
  function trainDefaults (line 122) | function trainDefaults(): INeuralNetworkTrainOptions {
  type INeuralNetworkData (line 140) | type INeuralNetworkData = number[] | Float32Array | Partial<INumberHash>;
  type INeuralNetworkDatum (line 143) | interface INeuralNetworkDatum<InputType, OutputType> {
  type INeuralNetworkDatumFormatted (line 148) | interface INeuralNetworkDatumFormatted<T> {
  class NeuralNetwork (line 153) | class NeuralNetwork<
    method constructor (line 198) | constructor(
    method initialize (line 214) | initialize(): void {
    method setActivation (line 254) | setActivation(activation?: NeuralNetworkActivation): void {
    method isRunnable (line 280) | get isRunnable(): boolean {
    method run (line 284) | run(input: Partial<InputType>): OutputType {
    method _runInputSigmoid (line 309) | _runInputSigmoid(input: Float32Array): Float32Array {
    method _runInputRelu (line 336) | _runInputRelu(input: Float32Array): Float32Array {
    method _runInputLeakyRelu (line 363) | _runInputLeakyRelu(input: Float32Array): Float32Array {
    method _runInputTanh (line 390) | _runInputTanh(input: Float32Array): Float32Array {
    method verifyIsInitialized (line 422) | verifyIsInitialized(
    method updateTrainingOptions (line 443) | updateTrainingOptions(trainOpts: Partial<INeuralNetworkTrainOptions>):...
    method validateTrainingOptions (line 450) | validateTrainingOptions(options: INeuralNetworkTrainOptions): void {
    method getTrainOptsJSON (line 529) | getTrainOptsJSON(): INeuralNetworkTrainOptionsJSON {
    method setLogMethod (line 569) | setLogMethod(log: boolean | ((state: INeuralNetworkState) => void)): v...
    method logTrainingStatus (line 579) | logTrainingStatus(status: INeuralNetworkState): void {
    method calculateTrainingError (line 585) | calculateTrainingError(
    method trainPatterns (line 595) | trainPatterns(data: Array<INeuralNetworkDatumFormatted<Float32Array>>)...
    method trainingTick (line 601) | trainingTick(
    method prepTraining (line 643) | prepTraining(
    method train (line 665) | train(
    method trainAsync (line 682) | async trainAsync(
    method trainPattern (line 707) | trainPattern(
    method _calculateDeltasSigmoid (line 724) | _calculateDeltasSigmoid(target: Float32Array): void {
    method _calculateDeltasRelu (line 750) | _calculateDeltasRelu(target: Float32Array): void {
    method _calculateDeltasLeakyRelu (line 776) | _calculateDeltasLeakyRelu(target: Float32Array): void {
    method _calculateDeltasTanh (line 803) | _calculateDeltasTanh(target: Float32Array): void {
    method adjustWeights (line 833) | adjustWeights(): void {
    method _setupAdam (line 859) | _setupAdam(): void {
    method _adjustWeightsAdam (line 885) | _adjustWeightsAdam(): void {
    method validateData (line 946) | validateData(data: Array<INeuralNetworkDatumFormatted<Float32Array>>):...
    method validateInput (line 965) | validateInput(formattedInput: Float32Array): void {
    method formatData (line 974) | formatData(
    method addFormat (line 1049) | addFormat(data: INeuralNetworkDatum<InputType, OutputType>): void {
    method test (line 1070) | test(
    method toJSON (line 1165) | toJSON(): INeuralNetworkJSON {
    method fromJSON (line 1197) | fromJSON(json: INeuralNetworkJSON): this {
    method toFunction (line 1233) | toFunction(

FILE: src/praxis/arthur-deviation-biases.ts
  type IUpdateThis (line 6) | interface IUpdateThis extends IKernelFunctionThis {
  function update (line 12) | function update(
  type IArthurDeviationBiasesSettings (line 23) | interface IArthurDeviationBiasesSettings extends IPraxisSettings {
  class ArthurDeviationBiases (line 31) | class ArthurDeviationBiases extends BasePraxis {
    method constructor (line 34) | constructor(layer: ILayer, settings?: IArthurDeviationBiasesSettings) {
    method run (line 40) | run(layer: ILayer): KernelOutput {
    method setupKernels (line 44) | setupKernels(): void {
  function arthurDeviationBiases (line 54) | function arthurDeviationBiases(

FILE: src/praxis/arthur-deviation-biases.unit.test.ts
  type I (line 63) | interface I {

FILE: src/praxis/arthur-deviation-weights.ts
  function updateChange (line 14) | function updateChange(value: number): number {
  type IUpdateConstants (line 18) | interface IUpdateConstants extends IConstantsThis {
  function update (line 23) | function update(
  type IArthurDeviationWeightsSettings (line 42) | interface IArthurDeviationWeightsSettings extends IPraxisSettings {
  type IKernelMapResults (line 50) | interface IKernelMapResults extends ISubKernelsResults {
  class ArthurDeviationWeights (line 62) | class ArthurDeviationWeights extends BasePraxis {
    method learningRate (line 66) | get learningRate(): number {
    method momentum (line 70) | get momentum(): number {
    method weightsLayer (line 74) | get weightsLayer(): ILayer {
    method weightsLayer (line 78) | set weightsLayer(layer: ILayer) {
    method deltaLayer (line 82) | get deltaLayer(): ILayer {
    method deltaLayer (line 86) | set deltaLayer(layer: ILayer) {
    method incomingLayer (line 90) | get incomingLayer(): ILayer {
    method incomingLayer (line 94) | set incomingLayer(layer: ILayer) {
    method constructor (line 98) | constructor(layer: ILayer, settings?: IArthurDeviationWeightsSettings) {
    method run (line 104) | run(): KernelOutput {
    method setupKernels (line 115) | setupKernels(): void {
  function arthurDeviationWeights (line 132) | function arthurDeviationWeights(

FILE: src/praxis/base-praxis.ts
  type ILayerTemplate (line 4) | interface ILayerTemplate {
  type IPraxisJSON (line 10) | interface IPraxisJSON {
  type IPraxisSettings (line 16) | interface IPraxisSettings {
  type IPraxis (line 23) | interface IPraxis {
  method width (line 42) | get width(): number {
  method height (line 46) | get height(): number {
  method depth (line 50) | get depth(): number {
  method constructor (line 54) | constructor(
  method setupKernels (line 63) | setupKernels(): void {}
  method reuseKernels (line 65) | reuseKernels(praxis: IPraxis): void {
  method toJSON (line 83) | toJSON(): Partial<IPraxisSettings> {

FILE: src/praxis/momentum-root-mean-squared-propagation.ts
  function getMomentum (line 14) | function getMomentum(
  function clipByValue (line 22) | function clipByValue(value: number, max: number, min: number): number {
  type IUpdate (line 32) | interface IUpdate extends IConstantsThis {
  function update (line 41) | function update(
  function isClippedByValue (line 68) | function isClippedByValue(
  type IMomentumRootMeanSquaredPropagationSettings (line 82) | interface IMomentumRootMeanSquaredPropagationSettings
  class MomentumRootMeanSquaredPropagation (line 99) | class MomentumRootMeanSquaredPropagation extends BasePraxis {
    method clipValue (line 104) | get clipValue(): number {
    method decayRate (line 108) | get decayRate(): number {
    method learningRate (line 112) | get learningRate(): number {
    method regularizationStrength (line 116) | get regularizationStrength(): number {
    method smoothEps (line 120) | get smoothEps(): number {
    method constructor (line 124) | constructor(
    method run (line 133) | run(layer: ILayer): KernelOutput {
    method setupKernels (line 142) | setupKernels(): void {
  function momentumRootMeanSquaredPropagation (line 164) | function momentumRootMeanSquaredPropagation(

FILE: src/recurrent.baseline.test.ts
  class MockRandomMatrix (line 25) | class MockRandomMatrix extends Matrix {
    method constructor (line 26) | constructor(rows: number, columns: number, std: number) {
  function asArrayOfArrayOfNumber (line 43) | function asArrayOfArrayOfNumber(v: KernelOutput | Input): number[][] {
  function asMatrix (line 49) | function asMatrix(v?: Matrix): Matrix {
  function setupNets (line 66) | function setupNets(): {
  function testRecurrentLayerSet (line 96) | function testRecurrentLayerSet(
  function testRecurrentLayerSet (line 256) | function testRecurrentLayerSet(index: number) {
  function testRecurrentModel (line 567) | function testRecurrentModel() {
  function testRecurrentLayerSetWeights (line 659) | function testRecurrentLayerSetWeights(

FILE: src/recurrent.end-to-end.test.ts
  function deltasAreZero (line 64) | function deltasAreZero() {
  function deltasAreSet (line 74) | function deltasAreSet() {
  function modelWeightsAreUpdated (line 84) | function modelWeightsAreUpdated() {
  function modelDeltasAreZero (line 104) | function modelDeltasAreZero() {

FILE: src/recurrent.ts
  type IRecurrentTrainingOptions (line 36) | interface IRecurrentTrainingOptions
  type IRecurrentOptions (line 41) | interface IRecurrentOptions extends IFeedForwardOptions {
  type IRecurrentPreppedTrainingData (line 51) | interface IRecurrentPreppedTrainingData<T> {
  class Recurrent (line 57) | class Recurrent<
    method constructor (line 70) | constructor(
    method _connectLayers (line 78) | _connectLayers(): {
    method _connectLayersDeep (line 102) | _connectLayersDeep(): ILayer[] {
    method _connectHiddenLayers (line 188) | _connectHiddenLayers(previousLayer: ILayer): ILayer[] {
    method initialize (line 207) | initialize(): void {
    method initializeDeep (line 230) | initializeDeep(): void {
    method run (line 242) | run(inputs: T[]): T[] {
    method runInput (line 253) | runInput(input: KernelOutput): KernelOutput {
    method runInputs (line 257) | runInputs(inputs: T[]): KernelOutput {
    method train (line 277) | train(
    method end (line 297) | end(): void {
    method transferData (line 308) | transferData(formattedData: T[][]): T[][] {
    method _prepTraining (line 314) | _prepTraining(
    method _calculateTrainingError (line 339) | _calculateTrainingError(data: T[][]): number {
    method formatData (line 363) | formatData(data: Float32Array): Float32Array {
    method _calculateDeltas (line 369) | _calculateDeltas(target: T[]): void {
    method adjustWeights (line 385) | adjustWeights(): void {
    method _trainPatterns (line 394) | _trainPatterns(data: T[][]): void {
    method _trainPattern (line 402) | _trainPattern(inputs: T[], logErrorRate: boolean): KernelOutput | null {

FILE: src/recurrent.unit.test.ts
  function copy2D (line 17) | function copy2D(matrix: Partial<Matrix> & number[][]) {
  class SuperLayer (line 235) | class SuperLayer extends Filter {
    method constructor (line 238) | constructor(inputLayer: ILayer) {
    method setupKernels (line 250) | setupKernels() {}
    method reuseKernels (line 252) | reuseKernels() {}
    method predict (line 254) | predict(inputs: KernelOutput) {
    method compare (line 258) | compare() {
    method learn (line 262) | learn() {}

FILE: src/recurrent/gru-time-step.ts
  class GRUTimeStep (line 7) | class GRUTimeStep extends RNNTimeStep {
    method getHiddenLayer (line 8) | getHiddenLayer(hiddenSize: number, prevSize: number): IRNNHiddenLayer {
    method getEquation (line 12) | getEquation(

FILE: src/recurrent/gru.test.ts
  function compare (line 111) | function compare(left: IMatrixJSON, right: IMatrixJSON) {

FILE: src/recurrent/gru.ts
  type IGRUHiddenLayer (line 6) | interface IGRUHiddenLayer extends IRNNHiddenLayer {
  class GRU (line 18) | class GRU extends RNN {
    method getHiddenLayer (line 19) | getHiddenLayer(hiddenSize: number, prevSize: number): IRNNHiddenLayer {
    method getEquation (line 23) | getEquation(
  function getGRUHiddenLayer (line 38) | function getGRUHiddenLayer(
  function getGRUEquation (line 61) | function getGRUEquation(

FILE: src/recurrent/lstm-time-step.test.ts
  class MockRandomMatrix (line 8) | class MockRandomMatrix {
    method rows (line 9) | get rows(): number {
    method columns (line 13) | get columns(): number {
    method weights (line 17) | get weights(): Float32Array {
    method weights (line 21) | set weights(weights: Float32Array) {
    method deltas (line 25) | get deltas(): Float32Array {
    method deltas (line 29) | set deltas(deltas: Float32Array) {
    method setWeight (line 33) | get setWeight(): (row: number, column: number, value: number) => void {
    method getWeight (line 37) | get getWeight(): (row: number, column: number) => number {
    method setDelta (line 41) | get setDelta(): (row: number, column: number, value: number) => void {
    method getDelta (line 45) | get getDelta(): (row: number, column: number) => number {
    method constructor (line 50) | constructor(rows: number, columns: number, std: number) {

FILE: src/recurrent/lstm-time-step.ts
  class LSTMTimeStep (line 7) | class LSTMTimeStep extends RNNTimeStep {
    method getHiddenLayer (line 8) | getHiddenLayer(hiddenSize: number, prevSize: number): IRNNHiddenLayer {
    method getEquation (line 12) | getEquation(

FILE: src/recurrent/lstm.test.ts
  function compare (line 50) | function compare(left: IMatrixJSON, right: Matrix) {

FILE: src/recurrent/lstm.ts
  type ILSTMHiddenLayer (line 6) | interface ILSTMHiddenLayer extends IRNNHiddenLayer {
  class LSTM (line 21) | class LSTM extends RNN {
    method getHiddenLayer (line 22) | getHiddenLayer(hiddenSize: number, prevSize: number): IRNNHiddenLayer {
    method getEquation (line 26) | getEquation(
  function getHiddenLSTMLayer (line 41) | function getHiddenLSTMLayer(
  function getLSTMEquation (line 67) | function getLSTMEquation(

FILE: src/recurrent/matrix/add-b.ts
  function addB (line 6) | function addB(product: Matrix, left: Matrix, right: Matrix): void {

FILE: src/recurrent/matrix/add.ts
  function add (line 6) | function add(product: Matrix, left: Matrix, right: Matrix): void {

FILE: src/recurrent/matrix/all-ones.ts
  function allOnes (line 6) | function allOnes(product: Matrix): void {

FILE: src/recurrent/matrix/clone-negative.ts
  function cloneNegative (line 3) | function cloneNegative(product: Matrix, left: Matrix): void {

FILE: src/recurrent/matrix/clone.ts
  function clone (line 3) | function clone(product: Matrix): Matrix {

FILE: src/recurrent/matrix/copy.ts
  function copy (line 3) | function copy(product: Matrix, left: Matrix): void {

FILE: src/recurrent/matrix/equation.test.ts
  function fourSquareMatrix (line 5) | function fourSquareMatrix(value: number): Matrix {
  function getState (line 13) | function getState(props: Partial<IState>): IState {

FILE: src/recurrent/matrix/equation.ts
  type PropagateIndex (line 20) | type PropagateIndex = (product: Matrix, left: Matrix, index: number) => ...
  type PropagateProduct (line 21) | type PropagateProduct = (product: Matrix) => void;
  type PropagateProductFromLeft (line 22) | type PropagateProductFromLeft = (product: Matrix, left: Matrix) => void;
  type PropagateProductFromLeftRight (line 23) | type PropagateProductFromLeftRight = (
  type PropagateFunction (line 28) | type PropagateFunction =
  type IState (line 34) | interface IState {
  class Equation (line 43) | class Equation {
    method add (line 48) | add(left: Matrix, right: Matrix): Matrix {
    method allOnes (line 67) | allOnes(rows: number, columns: number): Matrix {
    method cloneNegative (line 81) | cloneNegative(matrix: Matrix): Matrix {
    method subtract (line 98) | subtract(left: Matrix, right: Matrix): Matrix {
    method multiply (line 112) | multiply(left: Matrix, right: Matrix): Matrix {
    method multiplyElement (line 134) | multiplyElement(left: Matrix, right: Matrix): Matrix {
    method relu (line 156) | relu(matrix: Matrix): Matrix {
    method input (line 173) | input(input: Matrix): Matrix {
    method inputMatrixToRow (line 193) | inputMatrixToRow(matrix: Matrix): Matrix {
    method sigmoid (line 215) | sigmoid(matrix: Matrix): Matrix {
    method tanh (line 232) | tanh(matrix: Matrix): Matrix {
    method observe (line 250) | observe(matrix: Matrix): Matrix {
    method runIndex (line 264) | runIndex(rowIndex = 0): Matrix {
    method runInput (line 285) | runInput(inputValue: Float32Array): Matrix {
    method backpropagate (line 306) | backpropagate(): Matrix {
    method backpropagateIndex (line 327) | backpropagateIndex(rowIndex = 0): Matrix {
    method predictTarget (line 350) | predictTarget(input: Float32Array, target: Float32Array): number {
    method predictTargetIndex (line 368) | predictTargetIndex(input: number, target: number): number {

FILE: src/recurrent/matrix/index.ts
  type IMatrixJSON (line 3) | interface IMatrixJSON {
  class Matrix (line 11) | class Matrix {
    method constructor (line 17) | constructor(rows?: number, columns?: number) {
    method getWeight (line 25) | getWeight(row: number, col: number): number {
    method setWeight (line 37) | setWeight(row: number, col: number, v: number): Matrix {
    method getDelta (line 50) | getDelta(row: number, col: number): number {
    method setDelta (line 62) | setDelta(row: number, col: number, v: number): Matrix {
    method toJSON (line 75) | toJSON(): IMatrixJSON {
    method fromJSON (line 83) | static fromJSON(json: IMatrixJSON): Matrix {
    method fromArray (line 93) | static fromArray(weights: Float32Array[] | number[][]): Matrix {
    method deltasToArray (line 99) | deltasToArray(): number[][] {
    method weightsToArray (line 103) | weightsToArray(): number[][] {
    method toArray (line 107) | toArray(prop: 'weights' | 'deltas' = 'weights'): number[][] {
    method fromArray (line 124) | fromArray(
    method iterate (line 150) | iterate(callbacks: {

FILE: src/recurrent/matrix/max-i.ts
  function maxI (line 3) | function maxI(matrix: Matrix): number {

FILE: src/recurrent/matrix/multiply-b.ts
  function multiplyB (line 6) | function multiplyB(product: Matrix, left: Matrix, right: Matrix): void {

FILE: src/recurrent/matrix/multiply-element-b.ts
  function multiplyElementB (line 6) | function multiplyElementB(

FILE: src/recurrent/matrix/multiply-element.ts
  function multiplyElement (line 3) | function multiplyElement(

FILE: src/recurrent/matrix/multiply.ts
  function multiply (line 6) | function multiply(product: Matrix, left: Matrix, right: Matrix): void {

FILE: src/recurrent/matrix/ones-matrix.ts
  class OnesMatrix (line 6) | class OnesMatrix extends Matrix {
    method constructor (line 7) | constructor(rows: number, columns: number) {

FILE: src/recurrent/matrix/random-matrix.ts
  class RandomMatrix (line 6) | class RandomMatrix extends Matrix {
    method constructor (line 9) | constructor(rows: number, columns: number, std: number) {

FILE: src/recurrent/matrix/random-n-matrix.ts
  class RandomNMatrix (line 4) | class RandomNMatrix extends Matrix {
    method constructor (line 8) | constructor(rows: number, columns: number, mu: number, std: number) {
    method fillRandN (line 18) | fillRandN(): void {

FILE: src/recurrent/matrix/relu-b.ts
  function reluB (line 6) | function reluB(product: Matrix, left: Matrix): void {

FILE: src/recurrent/matrix/relu.ts
  function relu (line 7) | function relu(product: Matrix, left: Matrix): void {

FILE: src/recurrent/matrix/row-pluck-b.ts
  function rowPluckB (line 6) | function rowPluckB(

FILE: src/recurrent/matrix/row-pluck.ts
  function rowPluck (line 3) | function rowPluck(

FILE: src/recurrent/matrix/sample-i.ts
  function sampleI (line 4) | function sampleI(matrix: Matrix): number {

FILE: src/recurrent/matrix/sigmoid-b.ts
  function sigmoidB (line 3) | function sigmoidB(product: Matrix, left: Matrix): void {

FILE: src/recurrent/matrix/sigmoid.ts
  function sigmoid (line 3) | function sigmoid(product: Matrix, left: Matrix): void {

FILE: src/recurrent/matrix/softmax.ts
  function softmax (line 3) | function softmax(matrix: Matrix): Matrix {

FILE: src/recurrent/matrix/tanh-b.ts
  function tanhB (line 3) | function tanhB(product: Matrix, left: Matrix): void {

FILE: src/recurrent/matrix/tanh.ts
  function tanh (line 3) | function tanh(product: Matrix, left: Matrix): void {

FILE: src/recurrent/rnn-data-types.ts
  type Value (line 3) | type Value = string | number | boolean | string[] | number[] | boolean[];
  type IRNNDatum (line 5) | interface IRNNDatum {
  type ITimeStepObject (line 10) | interface ITimeStepObject {
  type TimeStepArray (line 14) | type TimeStepArray = number[];
  type TimeStepValue (line 16) | type TimeStepValue = Array<
  type ITimeStepRNNDatum (line 20) | interface ITimeStepRNNDatum {

FILE: src/recurrent/rnn-time-step.ts
  type ValuesOf (line 40) | type ValuesOf<
  type IRNNTimeStepOptions (line 44) | interface IRNNTimeStepOptions extends IRNNTimeStepJSONOptions {
  type IRNNTimeStepJSONOptions (line 57) | interface IRNNTimeStepJSONOptions {
  type IRNNTimeStepJSON (line 69) | interface IRNNTimeStepJSON {
  type IMisclass (line 81) | interface IMisclass {
  type ITestResults (line 86) | interface ITestResults {
  type IRNNTimeStepModel (line 92) | interface IRNNTimeStepModel {
  class RNNTimeStep (line 112) | class RNNTimeStep extends RNN {
    method constructor (line 133) | constructor(
    method createInputMatrix (line 148) | createInputMatrix(): RandomMatrix {
    method createOutputMatrices (line 152) | createOutputMatrices(): { outputConnector: RandomMatrix; output: Matri...
    method bindEquation (line 163) | bindEquation(): void {
    method initialize (line 200) | initialize(): void {
    method mapModel (line 206) | mapModel(): IRNNTimeStepModel {
    method backpropagate (line 236) | backpropagate(): void {
    method run (line 244) | run<InputType extends InputOutputValue | InputOutputValue[]>(
    method forecast (line 270) | forecast<InputType extends InputOutputValue | InputOutputValue[]>(
    method forecastArray (line 297) | forecastArray(input: Float32Array, count = 1): Float32Array {
    method forecastArrayOfArray (line 330) | forecastArrayOfArray(input: Float32Array[], count = 1): Float32Array[] {
    method forecastArrayOfObject (line 355) | forecastArrayOfObject(input: INumberHash[], count = 1): INumberHash[] {
    method train (line 376) | train(
    method trainArrayOfArray (line 429) | trainArrayOfArray(input: Float32Array[]): number {
    method trainPattern (line 447) | trainPattern(input: Float32Array[], logErrorRate?: boolean): number {
    method setSize (line 458) | setSize(data: FormattableData[]): void {
    method verifySize (line 498) | verifySize(): void {
    method runArray (line 506) | runArray(input: Float32Array): number {
    method runArrayOfArray (line 520) | runArrayOfArray(input: Float32Array[]): Float32Array {
    method runObject (line 536) | runObject(input: INumberHash): INumberHash {
    method runArrayOfObject (line 566) | runArrayOfObject(input: INumberHash[]): INumberHash {
    method runArrayOfObjectOfArray (line 585) | runArrayOfObjectOfArray(input: INumberHash[]): INumberHash {
    method end (line 600) | end(): void {
    method requireInputOutputOfOne (line 606) | requireInputOutputOfOne(): void {
    method formatArray (line 616) | formatArray(data: number[]): Float32Array[][] {
    method formatArrayOfArray (line 626) | formatArrayOfArray(data: number[][]): Float32Array[][] {
    method formatArrayOfObject (line 648) | formatArrayOfObject(data: INumberHash[]): Float32Array[][] {
    method formatArrayOfObjectMulti (line 663) | formatArrayOfObjectMulti(data: INumberHash[]): Float32Array[][] {
    method formatArrayOfDatumOfArray (line 679) | formatArrayOfDatumOfArray(data: ITrainingDatum[]): Float32Array[][] {
    method formatArrayOfDatumOfObject (line 695) | formatArrayOfDatumOfObject(data: ITrainingDatum[]): Float32Array[][] {
    method formatArrayOfArrayOfArray (line 721) | formatArrayOfArrayOfArray(data: number[][][]): Float32Array[][] {
    method formatArrayOfArrayOfObject (line 730) | formatArrayOfArrayOfObject(data: INumberHash[][]): Float32Array[][] {
    method formatArrayOfDatumOfArrayOfArray (line 754) | formatArrayOfDatumOfArrayOfArray(data: ITrainingDatum[]): Float32Array...
    method formatArrayOfDatumOfArrayOfObject (line 776) | formatArrayOfDatumOfArrayOfObject(
    method formatData (line 814) | formatData(data: FormattableData[]): Float32Array[][] {
    method test (line 849) | test(data: FormattableData[]): ITestResults {
    method addFormat (line 887) | addFormat(value: FormattableData): void {
    method toJSON (line 971) | toJSON(): IRNNTimeStepJSON {
    method fromJSON (line 1000) | fromJSON(json: IRNNTimeStepJSON): this {
    method toFunction (line 1046) | toFunction(cb?: (src: string) => string): RNNTimeStepFunction {
  type RNNTimeStepFunction (line 1323) | type RNNTimeStepFunction = <

FILE: src/recurrent/rnn.test.ts
  function notZero (line 7) | function notZero(v: number) {
  function xorNet (line 193) | function xorNet() {
  function checkExploded (line 315) | function checkExploded() {
  function compare (line 374) | function compare(left: IMatrixJSON, right: Matrix) {

FILE: src/recurrent/rnn.ts
  type IRNNModel (line 19) | interface IRNNModel {
  type IRNNOptions (line 30) | interface IRNNOptions {
  type IRNNJSONOptions (line 44) | interface IRNNJSONOptions {
  type IRNNTrainingOptions (line 57) | interface IRNNTrainingOptions {
  type IRNNJSONTrainOptions (line 68) | interface IRNNJSONTrainOptions {
  type IRNNHiddenLayer (line 89) | interface IRNNHiddenLayer {
  type IRNNHiddenLayerModel (line 93) | interface IRNNHiddenLayerModel extends IRNNHiddenLayer {
  type IRNNStatus (line 117) | interface IRNNStatus {
  type IRNNPreppedTrainingData (line 122) | interface IRNNPreppedTrainingData {
  class RNN (line 128) | class RNN {
    method constructor (line 147) | constructor(options: Partial<IRNNOptions & IRNNTrainingOptions> = {}) {
    method initialize (line 159) | initialize(): void {
    method createHiddenLayers (line 168) | createHiddenLayers(): IRNNHiddenLayer[] {
    method getHiddenLayer (line 184) | getHiddenLayer(hiddenSize: number, prevSize: number): IRNNHiddenLayer {
    method getEquation (line 195) | getEquation(
    method createInputMatrix (line 219) | createInputMatrix(): RandomMatrix {
    method createOutputMatrices (line 230) | createOutputMatrices(): { outputConnector: RandomMatrix; output: Matri...
    method bindEquation (line 244) | bindEquation(): void {
    method mapModel (line 283) | mapModel(): IRNNModel {
    method trainInput (line 318) | trainInput(input: number[]): number {
    method backpropagate (line 344) | backpropagate(input: number[]): void {
    method adjustWeights (line 355) | adjustWeights(): void {
    method isRunnable (line 391) | get isRunnable(): boolean {
    method checkRunnable (line 400) | checkRunnable(): void {
    method run (line 406) | run(rawInput: Value = [], isSampleI = false, temperature = 1): string {
    method verifyIsInitialized (line 487) | verifyIsInitialized(): void {
    method updateTrainingOptions (line 502) | updateTrainingOptions(options: Partial<IRNNTrainingOptions>): void {
    method validateTrainingOptions (line 510) | validateTrainingOptions(options: INeuralNetworkTrainOptions): void {
    method setLogMethod (line 555) | setLogMethod(log: Log | undefined | boolean): void {
    method prepTraining (line 565) | protected prepTraining(
    method train (line 587) | train(
    method addFormat (line 648) | addFormat(data: Value): void {}
    method formatData (line 650) | formatData(data: Value[]): number[][] {
    method toJSON (line 658) | toJSON(): IRNNJSON {
    method fromJSON (line 688) | fromJSON(json: IRNNJSON): this {
    method toFunction (line 740) | toFunction(cb?: (src: string) => string): RNNFunction {
    method trainPattern (line 959) | trainPattern(input: number[], logErrorRate?: boolean): number {
  type IRNNJSON (line 971) | interface IRNNJSON {
  function last (line 981) | function last<T>(values: T[]): T {
  type RNNFunction (line 985) | type RNNFunction = (

FILE: src/test-utils.ts
  function onePlusPlus3D (line 13) | function onePlusPlus3D(width: number, height: number, depth: number): nu...
  function onePlusPlus2D (line 30) | function onePlusPlus2D(width: number, height: number): number[][] {
  function zero3D (line 43) | function zero3D(width: number, height: number, depth: number): number[][...
  function zero2D (line 59) | function zero2D(width: number, height: number): number[][] {
  function allDeltas (line 90) | function allDeltas(model: any, fn: any): void {
  function allMatrices (line 109) | function allMatrices(model: any, fn: any): void {
  function shave (line 145) | function shave(value: Float32Array): Float32Array {
  function shave2D (line 153) | function shave2D(value: Float32Array[]): Float32Array[] {
  function shave3D (line 161) | function shave3D(value: Float32Array[][]): Float32Array[][] {
  function expectFunction (line 171) | function expectFunction(source: string, fn: Function): void {
  class TestLayer (line 175) | class TestLayer extends BaseLayer {
    method width (line 176) | get width(): number {
    method height (line 179) | get height(): number {
    method depth (line 182) | get depth(): number {
    method constructor (line 185) | constructor(settings: ILayerSettings) {
  function mockLayer (line 190) | function mockLayer(settings: ILayerSettings = {}): ILayer {
  function mockTexture (line 194) | function mockTexture(settings?: Partial<IGPUTextureSettings>): Texture {
  function mockPraxis (line 206) | function mockPraxis(layerTemplate: ILayerTemplate, praxisSettings: Parti...
  type IWithCompareKernel (line 228) | interface IWithCompareKernel {
  type IWithPredictKernel (line 232) | interface IWithPredictKernel {
  type IWithPredictKernelMap (line 236) | interface IWithPredictKernelMap {

FILE: src/utilities/array-lookup-table.ts
  class ArrayLookupTable (line 1) | class ArrayLookupTable {
    method constructor (line 5) | constructor(

FILE: src/utilities/cast.ts
  function arraysToFloat32Arrays (line 1) | function arraysToFloat32Arrays(arrays: number[][]): Float32Array[] {
  function inputOutputArraysToFloat32Arrays (line 9) | function inputOutputArraysToFloat32Arrays(
  function arrayToFloat32Arrays (line 23) | function arrayToFloat32Arrays(array: number[]): Float32Array[] {
  function inputOutputArrayToFloat32Arrays (line 31) | function inputOutputArrayToFloat32Arrays(
  function arrayToFloat32Array (line 45) | function arrayToFloat32Array(array: number[]): Float32Array {
  function objectsToFloat32Arrays (line 49) | function objectsToFloat32Arrays(
  function inputOutputObjectsToFloat32Arrays (line 68) | function inputOutputObjectsToFloat32Arrays(
  function objectToFloat32Arrays (line 100) | function objectToFloat32Arrays(
  function inputOutputObjectToFloat32Arrays (line 111) | function inputOutputObjectToFloat32Arrays(
  function objectToFloat32Array (line 127) | function objectToFloat32Array(

FILE: src/utilities/data-formatter.ts
  type IDataFormatter (line 3) | interface IDataFormatter {
  class DataFormatter (line 18) | class DataFormatter implements IDataFormatter {
    method constructor (line 25) | constructor(private values?: Array<IRNNDatum | Value>, maxThreshold = ...
    method setup (line 31) | setup(values: Array<IRNNDatum | Value>, maxThreshold = 0): void {
    method buildCharactersFromIterable (line 46) | buildCharactersFromIterable(values: Array<IRNNDatum | Value>): void {
    method addCharacters (line 124) | addCharacters(
    method buildTables (line 136) | buildTables(maxThreshold: number): void {
    method toIndexes (line 153) | toIndexes(value: Value, maxThreshold = 0): number[] {
    method toIndexesInputOutput (line 179) | toIndexesInputOutput(
    method toIndexesValue (line 190) | toIndexesValue(
    method toCharacters (line 215) | toCharacters(indices: number[], maxThreshold = 0): string[] {
    method toString (line 237) | toString(indices: number[], maxThreshold: number): string {
    method addInputOutput (line 241) | addInputOutput(): void {
    method addUnrecognized (line 246) | addUnrecognized(): void {
    method fromAllPrintable (line 250) | static fromAllPrintable(
    method fromAllPrintableInputOutput (line 260) | static fromAllPrintableInputOutput(
    method fromStringInputOutput (line 270) | static fromStringInputOutput(
    method fromArrayInputOutput (line 282) | static fromArrayInputOutput(
    method fromString (line 305) | static fromString(string: string, maxThreshold = 0): DataFormatter {
    method toJSON (line 310) | toJSON(): IDataFormatterJSON {
    method fromJSON (line 323) | static fromJSON(json: IDataFormatterJSON): DataFormatter {
    method addSpecial (line 334) | addSpecial(special: string | number, character = null): void {
    method toFunctionString (line 341) | toFunctionString(): string {
    method formatDataIn (line 354) | formatDataIn(input?: Value, output?: Value): number[] {
    method formatDataOut (line 365) | formatDataOut(input: number[], output: number[]): string {
    method format (line 369) | format(data: Array<IRNNDatum | Value>): number[][] {
  function validateAndCast (line 412) | function validateAndCast(value: Value): string | string[] {
  type IDataFormatterJSON (line 429) | interface IDataFormatterJSON {

FILE: src/utilities/flatten-layers.ts
  function flattenLayers (line 4) | function flattenLayers(layers: ILayer[]): ILayer[] {

FILE: src/utilities/kernel.ts
  function setup (line 22) | function setup(value: GPU): void {
  function teardown (line 29) | function teardown(): void {
  function makeKernel (line 36) | function makeKernel<
  function makeKernelMap (line 54) | function makeKernelMap<
  function kernelInput (line 87) | function kernelInput(value: number[], size: OutputDimensions): Input {
  function release (line 94) | function release(possibleTexture: KernelOutput | Input): void {
  function clear (line 103) | function clear(value: KernelOutput): void {
  function clone (line 141) | function clone(value: KernelOutput): KernelOutput {

FILE: src/utilities/layer-from-json.ts
  function layerFromJSON (line 13) | function layerFromJSON(

FILE: src/utilities/layer-setup.ts
  type IStride (line 3) | interface IStride {
  function getStride (line 8) | function getStride(
  type IPadding (line 27) | interface IPadding {
  function getPadding (line 32) | function getPadding(

FILE: src/utilities/layer-size.ts
  function checkSameSize (line 3) | function checkSameSize(layer1: ILayer, layer2: ILayer): void {

FILE: src/utilities/lookup-table.ts
  type LookupTableProp (line 3) | type LookupTableProp = 'input' | 'output';
  class LookupTable (line 5) | class LookupTable {
    method constructor (line 9) | constructor(

FILE: src/utilities/max.ts
  function max (line 1) | function max(

FILE: src/utilities/mse.ts
  function mse (line 1) | function mse(errors: Float32Array): number {

FILE: src/utilities/ones.ts
  function ones (line 1) | function ones(size: number): Float32Array {
  function ones2D (line 5) | function ones2D(width: number, height: number): Float32Array[] {

FILE: src/utilities/random-weight.ts
  function randomWeight (line 1) | function randomWeight(): number {

FILE: src/utilities/random.ts
  function randomFloat (line 6) | function randomFloat(min: number, max: number): number {
  function gaussRandom (line 14) | function gaussRandom(): number {
  function randomInteger (line 36) | function randomInteger(min: number, max: number): number {
  function randomN (line 45) | function randomN(mu: number, std: number): number {

FILE: src/utilities/randos.ts
  function randos (line 7) | function randos(size: number, std: number | null = null): Float32Array {
  function randos2D (line 24) | function randos2D(
  function randos3D (line 39) | function randos3D(

FILE: src/utilities/range.ts
  function range (line 7) | function range(start: number, end: number): number[] {

FILE: src/utilities/to-array.ts
  function toArray (line 1) | function toArray(

FILE: src/utilities/to-svg.ts
  type LineDrawInfo (line 18) | interface LineDrawInfo {
  type NodeDrawInfo (line 24) | interface NodeDrawInfo {
  type BaseDrawArgs (line 29) | interface BaseDrawArgs {
  type InputDrawArgs (line 37) | interface InputDrawArgs extends BaseDrawArgs {
  function drawInput (line 44) | function drawInput({
  type NeuronDrawArgs (line 81) | interface NeuronDrawArgs extends BaseDrawArgs {
  function drawNeuron (line 86) | function drawNeuron({
  type OutputDrawArgs (line 104) | interface OutputDrawArgs extends BaseDrawArgs {
  function drawOutput (line 110) | function drawOutput({
  type BackwardConnectionsDrawArgs (line 136) | interface BackwardConnectionsDrawArgs extends BaseDrawArgs {
  function drawBackwardConnections (line 143) | function drawBackwardConnections({
  type NeuralNetworkDrawOptions (line 162) | interface NeuralNetworkDrawOptions {
  function neuralNetworkToInnerSVG (line 175) | function neuralNetworkToInnerSVG(
  type RecurrentConnectionsDrawArgs (line 216) | interface RecurrentConnectionsDrawArgs extends BaseDrawArgs {
  function drawRecurrentConnections (line 221) | function drawRecurrentConnections({
  type RecurrentNeuralNetworkDrawOptions (line 247) | interface RecurrentNeuralNetworkDrawOptions
  function rnnToInnerSVG (line 252) | function rnnToInnerSVG(
  function getFeedForwardLayers (line 280) | function getFeedForwardLayers(network: FeedForward): ISimpleNet {
  function getRecurrentLayers (line 314) | function getRecurrentLayers(network: Recurrent): ISimpleNet {
  function wrapOuterSVG (line 341) | function wrapOuterSVG(
  function getNeuralNetworkJSONSizes (line 355) | function getNeuralNetworkJSONSizes(json: INeuralNetworkJSON): number[] {
  function getNeuralNetworkSizes (line 359) | function getNeuralNetworkSizes<
  function getRNNSizes (line 387) | function getRNNSizes(
  function defaultOptions (line 395) | function defaultOptions(): RecurrentNeuralNetworkDrawOptions {
  type ISimpleNet (line 429) | interface ISimpleNet {
  type ISizes (line 434) | interface ISizes {
  function toSVG (line 438) | function toSVG<
  function checkSizes (line 566) | function checkSizes(

FILE: src/utilities/traverse-layers-excluding-from.ts
  function traverseLayersExcludingFrom (line 3) | function traverseLayersExcludingFrom(

FILE: src/utilities/traverse-layers-from.ts
  function traverseLayersFrom (line 3) | function traverseLayersFrom(

FILE: src/utilities/values-2d.ts
  function values2D (line 6) | function values2D(

FILE: src/utilities/values-3d.ts
  function values3D (line 6) | function values3D(

FILE: src/utilities/values.ts
  function values (line 4) | function values(size: number, value: number): Float32Array {

FILE: src/utilities/zeros-2d.ts
  function zeros2D (line 6) | function zeros2D(width: number, height: number): Float32Array[] {

FILE: src/utilities/zeros-3d.ts
  function zeros3D (line 6) | function zeros3D(

FILE: src/utilities/zeros.ts
  function zeros (line 4) | function zeros(size: number): Float32Array {
Condensed preview — 220 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,013K chars).
[
  {
    "path": ".editorconfig",
    "chars": 171,
    "preview": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newli"
  },
  {
    "path": ".eslintignore",
    "chars": 246,
    "preview": "# Mac.\n.DS_STORE\n**.DS_STORE\n\n# Node.\nnode_modules\nnpm-debug.log\n\n# Yarn\nyarn.lock\n\n# parcel bundler cache\n.cache\n\n# cod"
  },
  {
    "path": ".eslintrc",
    "chars": 1425,
    "preview": "{\n  \"env\": {\n    \"es6\": true\n  },\n  \"extends\": [\n    \"eslint:recommended\",\n    \"standard-with-typescript\",\n    \"plugin:@"
  },
  {
    "path": ".gitattributes",
    "chars": 93,
    "preview": "dist/* linguist-vendored\r\nexamples/* linguist-documentation\r\n*.js linguist-detectable=false\r\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 944,
    "preview": "# Contribution Steps:\n\nThanks for taking the time to contribute to brain.js. Follow these guidelines to make the process"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 947,
    "preview": "---\r\nname: Bug Report 🐞\r\nabout: Create a report to help us improve.\r\nlabels: bug\r\n---\r\n\r\n<!-- If you don't mind add a fu"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 529,
    "preview": "---\r\nname: Feature Request 💡\r\nabout: Suggest a new idea for the project.\r\nlabels: enhancement\r\n---\r\n\r\n<!-- If you don't "
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 1895,
    "preview": "<!--- Provide a general summary of your changes in the Title above -->\n\n<!-- If you don't mind add a fun gif or meme, bu"
  },
  {
    "path": ".github/workflows/nodejs.yml",
    "chars": 2360,
    "preview": "name: CI\non:\n  pull_request:\n    branches: [master]\n  push:\n    branches: [master]\njobs:\n  build:\n    name: Build, lint,"
  },
  {
    "path": ".gitignore",
    "chars": 198,
    "preview": "# Mac.\n.DS_STORE\n**.DS_STORE\n\n# Node.\nnode_modules\nnpm-debug.log\n\n# Yarn\nyarn.lock\nyarn-error.log\n\n# parcel bundler cach"
  },
  {
    "path": ".npmignore",
    "chars": 271,
    "preview": "# Mac.\n.DS_STORE\n**.DS_STORE\n\n# Node.\nnode_modules\nnpm-debug.log\n\n# parcel bundler cache\n.cache\n\n# code coverage\n__cover"
  },
  {
    "path": ".npmrc",
    "chars": 35,
    "preview": "registry=https://registry.npmjs.org"
  },
  {
    "path": ".nvmrc",
    "chars": 8,
    "preview": "v22.4.3\n"
  },
  {
    "path": ".prettierrc",
    "chars": 85,
    "preview": "{\n  \"semi\": true,\n  \"singleQuote\": true,\n  \"tabWidth\": 2,\n  \"trailingComma\": \"es5\"\n}\n"
  },
  {
    "path": ".travis.yml",
    "chars": 178,
    "preview": "language: node_js\nnode_js:\n  - '10'\n  - '12'\ncache:\n  directories:\n    - node_modules\ninstall:\n  - npm ci\nscript:\n  - np"
  },
  {
    "path": ".vscode/launch.json",
    "chars": 842,
    "preview": "{\r\n  // Use IntelliSense to learn about possible attributes.\r\n  // Hover to view descriptions of existing attributes.\r\n "
  },
  {
    "path": "LICENSE",
    "chars": 1064,
    "preview": "Copyright (c) 2010-2019 Heather Arthur\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of "
  },
  {
    "path": "README.md",
    "chars": 27752,
    "preview": "<p align=\"center\">\n  <img src=\"https://cdn.rawgit.com/harthur-org/brain.js/ff595242/logo.svg\" alt=\"Logo\" width=200px/>\n<"
  },
  {
    "path": "jest.config.json",
    "chars": 326,
    "preview": "{\n  \"collectCoverage\": false,\n  \"collectCoverageFrom\": [\"src/**/*\"],\n  \"coverageDirectory\": \"__coverage__\",\n  \"coverageP"
  },
  {
    "path": "package.json",
    "chars": 3303,
    "preview": "{\n  \"author\": \"Heather Arthur <fayearthur@gmail.com>\",\n  \"browser\": \"dist/browser.js\",\n  \"bugs\": {\n    \"url\": \"https://g"
  },
  {
    "path": "rollup.config.browser.js",
    "chars": 1248,
    "preview": "import commonjs from '@rollup/plugin-commonjs';\nimport json from '@rollup/plugin-json';\nimport resolve from '@rollup/plu"
  },
  {
    "path": "rollup.config.js",
    "chars": 971,
    "preview": "import commonjs from '@rollup/plugin-commonjs';\nimport json from '@rollup/plugin-json';\nimport resolve from '@rollup/plu"
  },
  {
    "path": "src/README.md",
    "chars": 279,
    "preview": "# Brain.js Source Files\nWelcome!\n\nBrain.js aims to be:\n* Javascript and Node based\n* an easy, well thought out api\n* sim"
  },
  {
    "path": "src/activation/README.md",
    "chars": 982,
    "preview": "# [Activation](https://en.wikipedia.org/wiki/Activation_function)\n## What is activation?\nActivation can be thought of as"
  },
  {
    "path": "src/activation/index.test.ts",
    "chars": 506,
    "preview": "import * as activation from '../activation';\nimport * as leakyRelu from '../activation/leaky-relu';\nimport * as relu fro"
  },
  {
    "path": "src/activation/index.ts",
    "chars": 145,
    "preview": "export * as relu from './relu';\nexport * as sigmoid from './sigmoid';\nexport * as tanh from './tanh';\nexport * as leakyR"
  },
  {
    "path": "src/activation/leaky-relu.test.ts",
    "chars": 1159,
    "preview": "import * as leakyRelu from './leaky-relu';\n\ndescribe('leakyRelu', () => {\n  describe('.active()', () => {\n    describe('"
  },
  {
    "path": "src/activation/leaky-relu.ts",
    "chars": 395,
    "preview": "/**\n * Leaky Relu Activation, aka Leaky Rectified Linear Unit Activation\n * @description https://en.wikipedia.org/wiki/R"
  },
  {
    "path": "src/activation/relu.test.ts",
    "chars": 1079,
    "preview": "import * as relu from './relu';\n\ndescribe('relu', () => {\n  describe('.active()', () => {\n    describe('when weight is g"
  },
  {
    "path": "src/activation/relu.ts",
    "chars": 372,
    "preview": "/**\n * Relu Activation, aka Rectified Linear Unit Activation\n * @description https://en.wikipedia.org/wiki/Rectifier_(ne"
  },
  {
    "path": "src/activation/sigmoid.test.ts",
    "chars": 371,
    "preview": "import * as sigmoid from './sigmoid';\n\ndescribe('sigmoid', () => {\n  describe('.active()', () => {\n    it('matches for v"
  },
  {
    "path": "src/activation/sigmoid.ts",
    "chars": 257,
    "preview": "/**\n * sigmoid activation\n */\nexport function activate(value: number): number {\n  return 1 / (1 + Math.exp(-value));\n}\n\n"
  },
  {
    "path": "src/activation/tanh.test.ts",
    "chars": 370,
    "preview": "import * as tanh from './tanh';\n\ndescribe('tanh', () => {\n  describe('.active()', () => {\n    it('matches for value 1', "
  },
  {
    "path": "src/activation/tanh.ts",
    "chars": 273,
    "preview": "/**\n * Hyperbolic tan\n */\nexport function activate(weight: number): number {\n  return Math.tanh(weight);\n}\n\n/**\n * @desc"
  },
  {
    "path": "src/autoencoder.test.ts",
    "chars": 1652,
    "preview": "import AE from './autoencoder';\n\nconst trainingData = [\n  [0, 0, 0],\n  [0, 1, 1],\n  [1, 0, 1],\n  [1, 1, 0],\n];\n\nconst xo"
  },
  {
    "path": "src/autoencoder.ts",
    "chars": 6313,
    "preview": "import { KernelOutput, Texture, TextureArrayOutput } from 'gpu.js';\nimport {\n  IJSONLayer,\n  INeuralNetworkData,\n  INeur"
  },
  {
    "path": "src/cross-validate.test.ts",
    "chars": 12151,
    "preview": "import CrossValidate from './cross-validate';\nimport {\n  INeuralNetworkOptions,\n  INeuralNetworkTrainOptions,\n  NeuralNe"
  },
  {
    "path": "src/cross-validate.ts",
    "chars": 8979,
    "preview": "import {\n  INeuralNetworkBinaryTestResult,\n  INeuralNetworkState,\n  INeuralNetworkTestResult,\n} from './neural-network-t"
  },
  {
    "path": "src/errors/untrained-neural-network-error.ts",
    "chars": 241,
    "preview": "export class UntrainedNeuralNetworkError<\n  T extends { constructor: { name: string } }\n> extends Error {\n  constructor("
  },
  {
    "path": "src/estimator/mean-squared-error.ts",
    "chars": 1949,
    "preview": "import { IKernelRunShortcut, IKernelFunctionThis } from 'gpu.js';\nimport { makeKernel } from '../utilities/kernel';\n\nint"
  },
  {
    "path": "src/feed-forward.end-to-end.test.ts",
    "chars": 16934,
    "preview": "import { GPU } from 'gpu.js';\nimport { NeuralNetwork } from './neural-network';\nimport { FeedForward } from './feed-forw"
  },
  {
    "path": "src/feed-forward.ts",
    "chars": 23482,
    "preview": "import { IKernelFunctionThis, KernelOutput, Texture } from 'gpu.js';\nimport { MeanSquaredError } from './estimator/mean-"
  },
  {
    "path": "src/feed-forward.unit.test.ts",
    "chars": 20802,
    "preview": "import { GPU } from 'gpu.js';\nimport { setup, teardown } from './utilities/kernel';\nimport { FeedForward } from './feed-"
  },
  {
    "path": "src/index.ts",
    "chars": 1737,
    "preview": "import * as activation from './activation';\nimport { AE } from './autoencoder';\nimport CrossValidate from './cross-valid"
  },
  {
    "path": "src/layer/README.md",
    "chars": 1655,
    "preview": "# Layer\n\n## Basics\n### Memory\nA \"basic layer\" is composed of three types of Matrices which store what the neural network"
  },
  {
    "path": "src/layer/activation.test.ts",
    "chars": 2933,
    "preview": "import { Activation } from './activation';\nimport { ILayerSettings } from './base-layer';\nimport { mockPraxis, mockLayer"
  },
  {
    "path": "src/layer/activation.ts",
    "chars": 1061,
    "preview": "import { BaseLayer, ILayerSettings, ILayer } from './base-layer';\nimport { zeros2D } from '../utilities/zeros-2d';\nimpor"
  },
  {
    "path": "src/layer/add.test.ts",
    "chars": 6304,
    "preview": "import { GPU } from 'gpu.js';\nimport { gpuMock } from 'gpu-mock.js';\n\nimport { predict, Add, add } from './add';\nimport "
  },
  {
    "path": "src/layer/add.ts",
    "chars": 1622,
    "preview": "import { makeKernel, release, clone } from '../utilities/kernel';\nimport { checkSameSize } from '../utilities/layer-size"
  },
  {
    "path": "src/layer/arthur-feed-forward.ts",
    "chars": 2065,
    "preview": "import {\n  ArthurDeviationWeights,\n  arthurDeviationWeights,\n  IArthurDeviationWeightsSettings,\n} from '../praxis/arthur"
  },
  {
    "path": "src/layer/base-layer.test.ts",
    "chars": 1255,
    "preview": "import { ILayer } from './base-layer';\nimport { IPraxis, IPraxisSettings } from '../praxis/base-praxis';\nimport { mockLa"
  },
  {
    "path": "src/layer/base-layer.ts",
    "chars": 8888,
    "preview": "import {\n  IKernelRunShortcut,\n  Input,\n  Kernel,\n  KernelOutput,\n  Texture,\n  TextureArrayOutput,\n} from 'gpu.js';\nimpo"
  },
  {
    "path": "src/layer/convolution.test.ts",
    "chars": 5748,
    "preview": "import { GPU } from 'gpu.js';\nimport { gpuMock } from 'gpu-mock.js';\nimport {\n  predict,\n  compareFilterDeltas,\n  compar"
  },
  {
    "path": "src/layer/convolution.ts",
    "chars": 12620,
    "preview": "import { makeKernel, release, clone } from '../utilities/kernel';\nimport { getStride, getPadding } from '../utilities/la"
  },
  {
    "path": "src/layer/dropout.test.ts",
    "chars": 7992,
    "preview": "import { GPU } from 'gpu.js';\nimport { gpuMock } from 'gpu-mock.js';\n\nimport {\n  dropout,\n  Dropout,\n  trainingPredict,\n"
  },
  {
    "path": "src/layer/dropout.ts",
    "chars": 3082,
    "preview": "import { Filter, IFilterSettings } from './filter';\nimport { makeKernel, makeKernelMap, release } from '../utilities/ker"
  },
  {
    "path": "src/layer/feed-forward.test.ts",
    "chars": 407,
    "preview": "import { feedForward } from './feed-forward';\nimport { mockLayer } from '../test-utils';\n\ndescribe('FeedForward Layer', "
  },
  {
    "path": "src/layer/feed-forward.ts",
    "chars": 612,
    "preview": "import { random } from './random';\nimport { add } from './add';\nimport { multiply } from './multiply';\nimport { sigmoid "
  },
  {
    "path": "src/layer/filter.ts",
    "chars": 1429,
    "preview": "import { KernelOutput } from 'gpu.js';\nimport { BaseLayer, ILayer, ILayerSettings } from './base-layer';\n\nexport interfa"
  },
  {
    "path": "src/layer/fully-connected.test.ts",
    "chars": 9625,
    "preview": "import { GPU } from 'gpu.js';\nimport { gpuMock } from 'gpu-mock.js';\n\nimport {\n  predict,\n  predict3D,\n  compareBiases,\n"
  },
  {
    "path": "src/layer/fully-connected.ts",
    "chars": 8671,
    "preview": "import {\n  IConstantsThis,\n  IKernelFunctionThis,\n  IKernelRunShortcut,\n  KernelOutput,\n} from 'gpu.js';\nimport { Filter"
  },
  {
    "path": "src/layer/gru.ts",
    "chars": 1971,
    "preview": "import { add } from './add';\nimport { negative } from './negative';\nimport { multiply } from './multiply';\nimport { mult"
  },
  {
    "path": "src/layer/index.ts",
    "chars": 1739,
    "preview": "import {\n  Activation,\n  EntryPoint,\n  Filter,\n  Internal,\n  InternalModel,\n  Model,\n  Modifier,\n  Operator,\n  Target,\n}"
  },
  {
    "path": "src/layer/input.test.ts",
    "chars": 735,
    "preview": "import { GPU } from 'gpu.js';\nimport { Input } from './input';\nimport { setup, teardown } from '../utilities/kernel';\n\nd"
  },
  {
    "path": "src/layer/input.ts",
    "chars": 2205,
    "preview": "import { IKernelFunctionThis, IKernelRunShortcut, KernelOutput } from 'gpu.js';\nimport { EntryPoint } from './types';\nim"
  },
  {
    "path": "src/layer/internal.ts",
    "chars": 1398,
    "preview": "import { BaseLayer, ILayer, ILayerJSON, ILayerSettings } from './base-layer';\nimport { IKernelRunShortcut, Input, Kernel"
  },
  {
    "path": "src/layer/leaky-relu.test.ts",
    "chars": 7616,
    "preview": "import { gpuMock } from 'gpu-mock.js';\nimport { GPU } from 'gpu.js';\nimport * as leakyReluActivation from '../activation"
  },
  {
    "path": "src/layer/leaky-relu.ts",
    "chars": 2331,
    "preview": "import { Activation } from './types';\nimport { makeKernel, release } from '../utilities/kernel';\nimport { activate, meas"
  },
  {
    "path": "src/layer/lstm-cell.test.ts",
    "chars": 6660,
    "preview": "import {\n  Add,\n  lstmCell,\n  Multiply,\n  MultiplyElement,\n  Random,\n  RecurrentZeros,\n  Sigmoid,\n  Tanh,\n  Zeros,\n} fro"
  },
  {
    "path": "src/layer/lstm-cell.ts",
    "chars": 3268,
    "preview": "import { add } from './add';\nimport { multiply } from './multiply';\nimport { multiplyElement } from './multiply-element'"
  },
  {
    "path": "src/layer/modifier.ts",
    "chars": 1084,
    "preview": "import { BaseLayer, ILayer, ILayerSettings } from './base-layer';\n\nexport type ModifierType = new (\n  inputLayer: ILayer"
  },
  {
    "path": "src/layer/multiply-element.test.ts",
    "chars": 6837,
    "preview": "import { GPU } from 'gpu.js';\nimport { gpuMock } from 'gpu-mock.js';\n\nimport {\n  MultiplyElement,\n  multiplyElement,\n  p"
  },
  {
    "path": "src/layer/multiply-element.ts",
    "chars": 2107,
    "preview": "import { makeKernel, release } from '../utilities/kernel';\nimport { Operator } from './operator';\nimport { checkSameSize"
  },
  {
    "path": "src/layer/multiply.test.ts",
    "chars": 5938,
    "preview": "import { GPU } from 'gpu.js';\nimport { gpuMock } from 'gpu-mock.js';\nimport { Input } from './input';\nimport {\n  Multipl"
  },
  {
    "path": "src/layer/multiply.ts",
    "chars": 4366,
    "preview": "import { makeKernel, release } from '../utilities/kernel';\nimport { Operator } from './operator';\nimport {\n  IConstantsT"
  },
  {
    "path": "src/layer/negative.test.ts",
    "chars": 1492,
    "preview": "import { gpuMock } from 'gpu-mock.js';\nimport { input } from './input';\nimport { Negative, predict } from './negative';\n"
  },
  {
    "path": "src/layer/negative.ts",
    "chars": 901,
    "preview": "import { makeKernel } from '../utilities/kernel';\nimport { Modifier } from './types';\nimport { IKernelFunctionThis, IKer"
  },
  {
    "path": "src/layer/ones.ts",
    "chars": 490,
    "preview": "import { ILayerSettings } from './base-layer';\n\nimport { ones2D } from '../utilities/ones';\nimport { zeros2D } from '../"
  },
  {
    "path": "src/layer/operator.ts",
    "chars": 702,
    "preview": "import { BaseLayer, ILayerSettings, ILayer } from './base-layer';\nimport { zeros2D } from '../utilities/zeros-2d';\n\nexpo"
  },
  {
    "path": "src/layer/output.ts",
    "chars": 679,
    "preview": "import { add } from './add';\nimport { multiply } from './multiply';\nimport { random } from './random';\nimport { target }"
  },
  {
    "path": "src/layer/pool.test.ts",
    "chars": 6621,
    "preview": "import { GPU } from 'gpu.js';\nimport { gpuMock } from 'gpu-mock.js';\nimport {\n  Pool,\n  predict,\n  compare,\n  compare3D,"
  },
  {
    "path": "src/layer/pool.ts",
    "chars": 9524,
    "preview": "import { Filter } from './filter';\nimport { makeKernel, makeKernelMap, release } from '../utilities/kernel';\nimport { ge"
  },
  {
    "path": "src/layer/random.test.ts",
    "chars": 2643,
    "preview": "import { random, Random } from './random';\nimport { randos2D } from '../utilities/randos';\nimport { release } from '../u"
  },
  {
    "path": "src/layer/random.ts",
    "chars": 960,
    "preview": "import { randos2D } from '../utilities/randos';\nimport { zeros2D } from '../utilities/zeros-2d';\nimport { baseLayerDefau"
  },
  {
    "path": "src/layer/recurrent-connection.ts",
    "chars": 1950,
    "preview": "import { KernelOutput } from 'gpu.js';\n\nimport { Internal } from './internal';\nimport { release } from '../utilities/ker"
  },
  {
    "path": "src/layer/recurrent-input.ts",
    "chars": 2687,
    "preview": "import { KernelOutput } from 'gpu.js';\nimport { IPraxis } from '../praxis/base-praxis';\nimport { release } from '../util"
  },
  {
    "path": "src/layer/recurrent-zeros.ts",
    "chars": 1863,
    "preview": "import { IPraxis } from '../praxis/base-praxis';\nimport { release } from '../utilities/kernel';\nimport { zeros2D } from "
  },
  {
    "path": "src/layer/regression.ts",
    "chars": 1001,
    "preview": "import { IKernelFunctionThis, KernelOutput } from 'gpu.js';\n\nimport { BaseLayer, ILayer, ILayerSettings } from './base-l"
  },
  {
    "path": "src/layer/relu.test.ts",
    "chars": 7434,
    "preview": "import { GPU } from 'gpu.js';\nimport { gpuMock } from 'gpu-mock.js';\nimport { Relu, relu, predict2D, predict3D, compare2"
  },
  {
    "path": "src/layer/relu.ts",
    "chars": 2299,
    "preview": "import { IKernelFunctionThis, IKernelRunShortcut } from 'gpu.js';\n\nimport { Activation } from './types';\nimport { makeKe"
  },
  {
    "path": "src/layer/rnn-cell.test.ts",
    "chars": 2503,
    "preview": "import {\n  rnnCell,\n  RecurrentZeros,\n  Add,\n  Random,\n  Zeros,\n  Multiply,\n  Relu,\n} from './';\nimport { mockLayer, Tes"
  },
  {
    "path": "src/layer/rnn-cell.ts",
    "chars": 983,
    "preview": "import { add } from './add';\nimport { ILayer, ILayerSettings } from './base-layer';\nimport { multiply } from './multiply"
  },
  {
    "path": "src/layer/sigmoid.test.ts",
    "chars": 9374,
    "preview": "import { gpuMock } from 'gpu-mock.js';\nimport {\n  Sigmoid,\n  sigmoid,\n  predict2D,\n  predict3D,\n  compare2D,\n  compare3D"
  },
  {
    "path": "src/layer/sigmoid.ts",
    "chars": 2480,
    "preview": "import { ILayer, ILayerSettings } from './base-layer';\nimport { IKernelFunctionThis, IKernelRunShortcut } from 'gpu.js';"
  },
  {
    "path": "src/layer/soft-max.test.ts",
    "chars": 8663,
    "preview": "import { GPU } from 'gpu.js';\nimport { gpuMock } from 'gpu-mock.js';\n\nimport { setup, teardown } from '../utilities/kern"
  },
  {
    "path": "src/layer/soft-max.ts",
    "chars": 8325,
    "preview": "import {\n  IConstantsThis,\n  IKernelFunctionThis,\n  IKernelRunShortcut,\n  KernelOutput,\n  Texture,\n} from 'gpu.js';\n\nimp"
  },
  {
    "path": "src/layer/svm.ts",
    "chars": 961,
    "preview": "import { KernelOutput } from 'gpu.js';\n\nimport { BaseLayer, ILayer, ILayerSettings } from './base-layer';\nimport { clone"
  },
  {
    "path": "src/layer/tanh.test.ts",
    "chars": 9006,
    "preview": "import { GPU } from 'gpu.js';\nimport { gpuMock } from 'gpu-mock.js';\n\nimport { Tanh, tanh, predict2D, predict3D, compare"
  },
  {
    "path": "src/layer/tanh.ts",
    "chars": 2305,
    "preview": "import { IKernelFunctionThis, IKernelRunShortcut } from 'gpu.js';\n\nimport { Activation } from './activation';\nimport { a"
  },
  {
    "path": "src/layer/target.test.ts",
    "chars": 1941,
    "preview": "import { GPU } from 'gpu.js';\n\nimport { compare1D, compare2D, Target } from './target';\nimport { setup, teardown, makeKe"
  },
  {
    "path": "src/layer/target.ts",
    "chars": 2708,
    "preview": "import { IKernelFunctionThis, IKernelRunShortcut, KernelOutput } from 'gpu.js';\n\nimport { makeKernel, release, clone } f"
  },
  {
    "path": "src/layer/transpose.ts",
    "chars": 1171,
    "preview": "import { IKernelFunctionThis, IKernelRunShortcut } from 'gpu.js';\nimport { makeKernel } from '../utilities/kernel';\nimpo"
  },
  {
    "path": "src/layer/types.ts",
    "chars": 1026,
    "preview": "import { release } from '../utilities/kernel';\nimport { BaseLayer, ILayer, ILayerSettings } from './base-layer';\nexport "
  },
  {
    "path": "src/layer/zeros.ts",
    "chars": 687,
    "preview": "import { zeros2D } from '../utilities/zeros-2d';\nimport { Model } from './types';\nimport { ILayerSettings } from './base"
  },
  {
    "path": "src/likely.test.ts",
    "chars": 2170,
    "preview": "import { likely } from './likely';\nimport { NeuralNetwork } from './neural-network';\n\n/**\n * Return 0 or 1 for '#'\n */\nf"
  },
  {
    "path": "src/likely.ts",
    "chars": 823,
    "preview": "export interface ILikelyNet<InputType, OutputType> {\n  run: (input: InputType) => OutputType;\n}\n\nexport function likely<"
  },
  {
    "path": "src/lookup.test.ts",
    "chars": 3899,
    "preview": "import { lookup } from './lookup';\n\ndescribe('lookup', () => {\n  it('toHash()', () => {\n    const lup = lookup.toHash({ "
  },
  {
    "path": "src/lookup.ts",
    "chars": 6997,
    "preview": "import { KernelOutput } from 'gpu.js';\n\nexport interface INumberHash {\n  [character: string]: number;\n}\n\nexport interfac"
  },
  {
    "path": "src/neural-network-gpu.end-to-end.test.ts",
    "chars": 455,
    "preview": "import { NeuralNetworkGPU } from './neural-network-gpu';\nimport { xorTrainingData } from './test-utils';\n\ndescribe('Neur"
  },
  {
    "path": "src/neural-network-gpu.test.ts",
    "chars": 7681,
    "preview": "import { Texture } from 'gpu.js';\n\nimport { NeuralNetwork } from './neural-network';\nimport { NeuralNetworkGPU } from '."
  },
  {
    "path": "src/neural-network-gpu.ts",
    "chars": 19622,
    "preview": "import {\n  alias,\n  GPU,\n  GPUFunction,\n  IKernelFunctionThis,\n  IKernelMapRunShortcut,\n  IMappedKernelResult,\n  KernelO"
  },
  {
    "path": "src/neural-network-types.ts",
    "chars": 2240,
    "preview": "/** TODO: The following should be moved to neural-network.ts once that is converted to typescript. Added here until neur"
  },
  {
    "path": "src/neural-network.bitwise.test.ts",
    "chars": 4520,
    "preview": "import { NeuralNetwork } from './neural-network';\n\ndescribe('NeuralNetwork bitwise', () => {\n  describe('bitwise functio"
  },
  {
    "path": "src/neural-network.json.test.ts",
    "chars": 33896,
    "preview": "import { NeuralNetwork, defaults, trainDefaults } from './neural-network';\n\ndescribe('JSON', () => {\n  describe('.toJSON"
  },
  {
    "path": "src/neural-network.options.test.ts",
    "chars": 4393,
    "preview": "import { NeuralNetwork } from './neural-network';\n\ndescribe('NeuralNetwork', () => {\n  describe('options', () => {\n    i"
  },
  {
    "path": "src/neural-network.test-method.test.ts",
    "chars": 4260,
    "preview": "import { NeuralNetwork } from './neural-network';\nimport { INeuralNetworkBinaryTestResult } from './neural-network-types"
  },
  {
    "path": "src/neural-network.to-function.test.ts",
    "chars": 4105,
    "preview": "import { NeuralNetwork } from './neural-network';\nimport { INumberHash } from './lookup';\nimport { xorTrainingData } fro"
  },
  {
    "path": "src/neural-network.trainopts.test.ts",
    "chars": 11985,
    "preview": "import { NeuralNetwork } from './neural-network';\n\nconst data = [\n  { input: [0, 0], output: [0] },\n  { input: [0, 1], o"
  },
  {
    "path": "src/neural-network.ts",
    "chars": 40584,
    "preview": "import { Thaw } from 'thaw.js';\nimport { ITrainingStatus } from './feed-forward';\nimport { INumberHash, lookup } from '."
  },
  {
    "path": "src/neural-network.unit.test.ts",
    "chars": 2810,
    "preview": "import { getTypedArrayFn, NeuralNetwork } from './neural-network';\n\ndescribe('NeuralNetwork', () => {\n  describe('valida"
  },
  {
    "path": "src/praxis/README.md",
    "chars": 2292,
    "preview": "# [Praxis](https://en.wikipedia.org/wiki/Praxis_(process))\nModels to assist in helping neural networks improve their abi"
  },
  {
    "path": "src/praxis/arthur-deviation-biases.end-to-end.test.ts",
    "chars": 2233,
    "preview": "import { GPU } from 'gpu.js';\nimport { ArthurDeviationBiases } from './arthur-deviation-biases';\nimport { random } from "
  },
  {
    "path": "src/praxis/arthur-deviation-biases.ts",
    "chars": 1572,
    "preview": "import { makeKernel } from '../utilities/kernel';\nimport { BasePraxis, IPraxisSettings } from './base-praxis';\nimport { "
  },
  {
    "path": "src/praxis/arthur-deviation-biases.unit.test.ts",
    "chars": 2316,
    "preview": "import { gpuMock } from 'gpu-mock.js';\nimport { GPU, IKernelRunShortcut } from 'gpu.js';\nimport {\n  ArthurDeviationBiase"
  },
  {
    "path": "src/praxis/arthur-deviation-weights.end-to-end.test.ts",
    "chars": 4219,
    "preview": "import { GPU } from 'gpu.js';\nimport { ArthurDeviationWeights } from './';\nimport { random } from '../layer';\nimport { N"
  },
  {
    "path": "src/praxis/arthur-deviation-weights.ts",
    "chars": 3535,
    "preview": "import { makeKernelMap } from '../utilities/kernel';\nimport { zeros2D } from '../utilities/zeros-2d';\nimport { BasePraxi"
  },
  {
    "path": "src/praxis/arthur-deviation-weights.unit.test.ts",
    "chars": 4471,
    "preview": "import { GPU, IKernelRunShortcut } from 'gpu.js';\nimport { gpuMock } from 'gpu-mock.js';\nimport {\n  ArthurDeviationWeigh"
  },
  {
    "path": "src/praxis/base-praxis.ts",
    "chars": 2019,
    "preview": "import { ILayer } from '../layer';\nimport { IKernelRunShortcut, KernelOutput } from 'gpu.js';\n\nexport interface ILayerTe"
  },
  {
    "path": "src/praxis/index.ts",
    "chars": 353,
    "preview": "export {\n  ArthurDeviationBiases,\n  arthurDeviationBiases,\n} from './arthur-deviation-biases';\nexport {\n  ArthurDeviatio"
  },
  {
    "path": "src/praxis/momentum-root-mean-squared-propagation.test.ts",
    "chars": 1594,
    "preview": "import { GPU } from 'gpu.js';\n\nimport { MomentumRootMeanSquaredPropagation } from './momentum-root-mean-squared-propagat"
  },
  {
    "path": "src/praxis/momentum-root-mean-squared-propagation.ts",
    "chars": 4438,
    "preview": "import { BasePraxis, ILayerTemplate, IPraxisSettings } from './base-praxis';\n\nimport { makeKernelMap, release } from '.."
  },
  {
    "path": "src/recurrent/gru-time-step.test.ts",
    "chars": 705,
    "preview": "import { RNNTimeStep } from './rnn-time-step';\nimport { GRUTimeStep } from './gru-time-step';\n\ndescribe('GRUTimeStep', ("
  },
  {
    "path": "src/recurrent/gru-time-step.ts",
    "chars": 697,
    "preview": "import { getGRUHiddenLayer, getGRUEquation, IGRUHiddenLayer } from './gru';\nimport { Matrix } from './matrix';\nimport { "
  },
  {
    "path": "src/recurrent/gru.test.ts",
    "chars": 6823,
    "preview": "import { GRU } from './gru';\nimport { IMatrixJSON } from './matrix';\nimport { RNN } from './rnn';\nimport { DataFormatter"
  },
  {
    "path": "src/recurrent/gru.ts",
    "chars": 3895,
    "preview": "import { Matrix } from './matrix';\nimport { Equation } from './matrix/equation';\nimport { RandomMatrix } from './matrix/"
  },
  {
    "path": "src/recurrent/lstm-time-step.end-to-end.test.ts",
    "chars": 873,
    "preview": "import { LSTMTimeStep } from './lstm-time-step';\njest.retryTimes(5);\ndescribe('LSTMTimeStep', () => {\n  it('can learn xo"
  },
  {
    "path": "src/recurrent/lstm-time-step.test.ts",
    "chars": 5208,
    "preview": "import { RNNTimeStep } from './rnn-time-step';\nimport { LSTMTimeStep } from './lstm-time-step';\nimport { getHiddenLSTMLa"
  },
  {
    "path": "src/recurrent/lstm-time-step.ts",
    "chars": 705,
    "preview": "import { getHiddenLSTMLayer, getLSTMEquation, ILSTMHiddenLayer } from './lstm';\nimport { Matrix } from './matrix';\nimpor"
  },
  {
    "path": "src/recurrent/lstm.test.ts",
    "chars": 6115,
    "preview": "import { LSTM } from './lstm';\nimport { IMatrixJSON, Matrix } from './matrix';\nimport { RNN } from './rnn';\nimport { Dat"
  },
  {
    "path": "src/recurrent/lstm.ts",
    "chars": 4187,
    "preview": "import { Matrix } from './matrix';\nimport { Equation } from './matrix/equation';\nimport { RandomMatrix } from './matrix/"
  },
  {
    "path": "src/recurrent/matrix/add-b.ts",
    "chars": 302,
    "preview": "import { Matrix } from '.';\n\n/**\n * adds {from} deltas to {left} and {right} deltas\n */\nexport function addB(product: Ma"
  },
  {
    "path": "src/recurrent/matrix/add.ts",
    "chars": 308,
    "preview": "import { Matrix } from '.';\n\n/**\n * add {left} and {right} matrix weights into {into}\n */\nexport function add(product: M"
  },
  {
    "path": "src/recurrent/matrix/all-ones.ts",
    "chars": 244,
    "preview": "import { Matrix } from '.';\n\n/**\n * makes matrix weights and deltas all ones\n */\nexport function allOnes(product: Matrix"
  },
  {
    "path": "src/recurrent/matrix/clone-negative.ts",
    "chars": 371,
    "preview": "import { Matrix } from '.';\n\nexport function cloneNegative(product: Matrix, left: Matrix): void {\n  product.rows = left."
  },
  {
    "path": "src/recurrent/matrix/clone.ts",
    "chars": 300,
    "preview": "import { Matrix } from '.';\n\nfunction clone(product: Matrix): Matrix {\n  const cloned = new Matrix();\n\n  cloned.rows = p"
  },
  {
    "path": "src/recurrent/matrix/copy.ts",
    "chars": 237,
    "preview": "import { Matrix } from '.';\n\nexport function copy(product: Matrix, left: Matrix): void {\n  product.rows = left.rows;\n  p"
  },
  {
    "path": "src/recurrent/matrix/equation.test.ts",
    "chars": 7144,
    "preview": "import { Matrix } from './';\nimport { OnesMatrix } from './ones-matrix';\nimport { Equation, IState } from './equation';\n"
  },
  {
    "path": "src/recurrent/matrix/equation.ts",
    "chars": 8931,
    "preview": "import { Matrix } from '.';\nimport { add } from './add';\nimport { addB } from './add-b';\nimport { allOnes } from './all-"
  },
  {
    "path": "src/recurrent/matrix/index.test.ts",
    "chars": 8545,
    "preview": "import { Matrix } from './';\n\ndescribe('Matrix', () => {\n  describe('.constructor()', () => {\n    describe('.rows', () ="
  },
  {
    "path": "src/recurrent/matrix/index.ts",
    "chars": 4262,
    "preview": "import { zeros } from '../../utilities/zeros';\n\nexport interface IMatrixJSON {\n  rows: number;\n  columns: number;\n  weig"
  },
  {
    "path": "src/recurrent/matrix/max-i.ts",
    "chars": 324,
    "preview": "import { Matrix } from '.';\n\nexport function maxI(matrix: Matrix): number {\n  // argmax of array w\n  const { weights } ="
  },
  {
    "path": "src/recurrent/matrix/multiply-b.ts",
    "chars": 1108,
    "preview": "import { Matrix } from '.';\n\n/**\n * multiplies {from} deltas to {left} and {right}\n */\nexport function multiplyB(product"
  },
  {
    "path": "src/recurrent/matrix/multiply-element-b.ts",
    "chars": 394,
    "preview": "import { Matrix } from '.';\n\n/**\n * multiplies {left} and {right} weight by {from} deltas into {left} and {right} deltas"
  },
  {
    "path": "src/recurrent/matrix/multiply-element.ts",
    "chars": 291,
    "preview": "import { Matrix } from '.';\n\nexport function multiplyElement(\n  product: Matrix,\n  left: Matrix,\n  right: Matrix\n): void"
  },
  {
    "path": "src/recurrent/matrix/multiply.ts",
    "chars": 1108,
    "preview": "import { Matrix } from '.';\n\n/**\n * multiply {left} and {right} matrix weights to {into}\n */\nexport function multiply(pr"
  },
  {
    "path": "src/recurrent/matrix/ones-matrix.ts",
    "chars": 356,
    "preview": "import { Matrix } from '.';\nimport { ones } from '../../utilities/ones';\n\n/** return Matrix of ones\n */\nexport class One"
  },
  {
    "path": "src/recurrent/matrix/random-matrix.ts",
    "chars": 440,
    "preview": "import { Matrix } from '.';\nimport { randomFloat } from '../../utilities/random';\n\n/** return Matrix but filled with ran"
  },
  {
    "path": "src/recurrent/matrix/random-n-matrix.ts",
    "chars": 513,
    "preview": "import { Matrix } from '.';\nimport { randomN } from '../../utilities/random';\n\nexport class RandomNMatrix extends Matrix"
  },
  {
    "path": "src/recurrent/matrix/relu-b.ts",
    "chars": 308,
    "preview": "import { Matrix } from '.';\n\n/**\n * adds {from} deltas to {m} deltas when {m} weights are above other a threshold of 0\n "
  },
  {
    "path": "src/recurrent/matrix/relu.ts",
    "chars": 284,
    "preview": "import { Matrix } from '.';\n\n/**\n *\n * relu {m} weights to {into} weights\n */\nexport function relu(product: Matrix, left"
  },
  {
    "path": "src/recurrent/matrix/row-pluck-b.ts",
    "chars": 352,
    "preview": "import { Matrix } from '.';\n\n/**\n * adds {from} deltas into {m} deltas\n */\nexport function rowPluckB(\n  product: Matrix,"
  },
  {
    "path": "src/recurrent/matrix/row-pluck.ts",
    "chars": 349,
    "preview": "import { Matrix } from '.';\n\nexport function rowPluck(\n  product: Matrix,\n  left: Matrix,\n  rowPluckIndex: number\n): voi"
  },
  {
    "path": "src/recurrent/matrix/sample-i.ts",
    "chars": 384,
    "preview": "import { Matrix } from '.';\nimport { randomFloat } from '../../utilities/random';\n\nexport function sampleI(matrix: Matri"
  },
  {
    "path": "src/recurrent/matrix/sigmoid-b.ts",
    "chars": 245,
    "preview": "import { Matrix } from '.';\n\nexport function sigmoidB(product: Matrix, left: Matrix): void {\n  for (let i = 0; i < produ"
  },
  {
    "path": "src/recurrent/matrix/sigmoid.ts",
    "chars": 373,
    "preview": "import { Matrix } from '.';\n\nexport function sigmoid(product: Matrix, left: Matrix): void {\n  // sigmoid nonlinearity\n  "
  },
  {
    "path": "src/recurrent/matrix/softmax.ts",
    "chars": 713,
    "preview": "import { Matrix } from '.';\n\nexport function softmax(matrix: Matrix): Matrix {\n  // probability volume\n  const result = "
  },
  {
    "path": "src/recurrent/matrix/tanh-b.ts",
    "chars": 283,
    "preview": "import { Matrix } from '.';\n\nexport function tanhB(product: Matrix, left: Matrix): void {\n  for (let i = 0; i < product."
  },
  {
    "path": "src/recurrent/matrix/tanh.ts",
    "chars": 248,
    "preview": "import { Matrix } from '.';\n\nexport function tanh(product: Matrix, left: Matrix): void {\n  // tanh nonlinearity\n  for (l"
  },
  {
    "path": "src/recurrent/rnn-data-types.ts",
    "chars": 573,
    "preview": "/** TODO: might need to be extended to include string[][] */\n// type Values = string[] | number[] | string;\nexport type "
  },
  {
    "path": "src/recurrent/rnn-time-step.test.ts",
    "chars": 103654,
    "preview": "import { INumberHash } from '../lookup';\nimport { LSTMTimeStep } from './lstm-time-step';\nimport { Matrix } from './matr"
  },
  {
    "path": "src/recurrent/rnn-time-step.ts",
    "chars": 41192,
    "preview": "import {\n  FormattableData,\n  InputOutputValue,\n  INumberArray,\n  INumberHash,\n  ITrainingDatum,\n  lookup,\n} from '../lo"
  },
  {
    "path": "src/recurrent/rnn.test.ts",
    "chars": 21657,
    "preview": "import { IMatrixJSON, Matrix } from './matrix';\nimport { Equation } from './matrix/equation';\nimport { defaults, RNN, RN"
  },
  {
    "path": "src/recurrent/rnn.ts",
    "chars": 28748,
    "preview": "import { Log } from '../feed-forward';\nimport { INeuralNetworkTrainOptions } from '../neural-network';\nimport {\n  DataFo"
  },
  {
    "path": "src/recurrent.baseline.test.ts",
    "chars": 35638,
    "preview": "import { GPU, Input, KernelOutput } from 'gpu.js';\nimport { Matrix } from './recurrent/matrix';\nimport { input, output, "
  },
  {
    "path": "src/recurrent.end-to-end.test.ts",
    "chars": 10726,
    "preview": "import { GPU } from 'gpu.js';\nimport {\n  add,\n  random,\n  input,\n  lstmCell,\n  multiply,\n  output,\n  rnnCell,\n  ILayer,\n"
  },
  {
    "path": "src/recurrent.ts",
    "chars": 12937,
    "preview": "import { RecurrentConnection } from './layer/recurrent-connection';\nimport {\n  IRecurrentInput,\n  RecurrentInput,\n  Recu"
  },
  {
    "path": "src/recurrent.unit.test.ts",
    "chars": 11854,
    "preview": "import { GPU, KernelOutput } from 'gpu.js';\nimport {\n  add,\n  input,\n  multiply,\n  output,\n  random,\n  rnnCell,\n  IRecur"
  },
  {
    "path": "src/test-utils.ts",
    "chars": 6581,
    "preview": "import assert from 'assert';\nimport { IGPUTextureSettings, Kernel, Texture } from 'gpu.js';\nimport { ILayerTemplate, IPr"
  },
  {
    "path": "src/utilities/array-lookup-table.ts",
    "chars": 659,
    "preview": "export class ArrayLookupTable {\n  length = 0;\n  table: { [key: string]: number } = {};\n\n  constructor(\n    data: Array<{"
  },
  {
    "path": "src/utilities/cast.test.ts",
    "chars": 1929,
    "preview": "import * as cast from './cast';\nimport { LookupTable } from './lookup-table';\n\ndescribe('cast', () => {\n  describe('arra"
  },
  {
    "path": "src/utilities/cast.ts",
    "chars": 3675,
    "preview": "export function arraysToFloat32Arrays(arrays: number[][]): Float32Array[] {\n  const result: Float32Array[] = [];\n  for ("
  },
  {
    "path": "src/utilities/data-formatter.test.ts",
    "chars": 11894,
    "preview": "import { DataFormatter } from './data-formatter';\n\ndescribe('DataFormatter', () => {\n  describe('.fromJSON()', () => {\n "
  },
  {
    "path": "src/utilities/data-formatter.ts",
    "chars": 14330,
    "preview": "import { Value, IRNNDatum } from '../recurrent/rnn-data-types';\n\nexport interface IDataFormatter {\n  indexTable: { [valu"
  },
  {
    "path": "src/utilities/flatten-layers.test.ts",
    "chars": 1372,
    "preview": "import { flattenLayers } from './flatten-layers';\nimport { mockLayer } from '../test-utils';\n\ndescribe('flattenLayers', "
  },
  {
    "path": "src/utilities/flatten-layers.ts",
    "chars": 460,
    "preview": "import { ILayer } from '../layer/base-layer';\nimport { traverseLayersFrom } from './traverse-layers-from';\n\nexport funct"
  },
  {
    "path": "src/utilities/kernel.ts",
    "chars": 4230,
    "preview": "import {\n  GPU,\n  IConstantsThis,\n  IGPUKernelSettings,\n  IKernelMapRunShortcut,\n  IKernelRunShortcut,\n  Input,\n  ISubKe"
  },
  {
    "path": "src/utilities/layer-from-json.test.ts",
    "chars": 3502,
    "preview": "import {\n  Add,\n  Convolution,\n  ILayerJSON,\n  RecurrentZeros,\n  Sigmoid,\n  Target,\n} from '../layer';\nimport { layerFro"
  },
  {
    "path": "src/utilities/layer-from-json.ts",
    "chars": 2164,
    "preview": "import * as layer from '../layer';\nimport { layerTypes, ILayerJSON, ILayer, Target } from '../layer';\nimport { Activatio"
  },
  {
    "path": "src/utilities/layer-setup.ts",
    "chars": 1343,
    "preview": "import { IConvolutionSettingsBase } from '../layer/convolution';\n\nexport interface IStride {\n  strideX: number;\n  stride"
  },
  {
    "path": "src/utilities/layer-size.test.ts",
    "chars": 1433,
    "preview": "import { BaseLayer, ILayer } from '../layer';\nimport { checkSameSize } from './layer-size';\n\nlet layer: ILayer;\nlet laye"
  },
  {
    "path": "src/utilities/layer-size.ts",
    "chars": 403,
    "preview": "import { ILayer } from '../layer/base-layer';\n\nexport function checkSameSize(layer1: ILayer, layer2: ILayer): void {\n  i"
  },
  {
    "path": "src/utilities/lookup-table.ts",
    "chars": 1556,
    "preview": "import { InputOutputValue, INumberHash, ITrainingDatum } from '../lookup';\n\nexport type LookupTableProp = 'input' | 'out"
  },
  {
    "path": "src/utilities/max.test.ts",
    "chars": 185,
    "preview": "import { max } from './max';\n\ndescribe('max', () => {\n  test('should find max in object', () => {\n    const obj = { a: 1"
  },
  {
    "path": "src/utilities/max.ts",
    "chars": 270,
    "preview": "export function max(\n  values:\n    | Float32Array\n    | {\n        [key: string]: number;\n      }\n): number {\n  if (Array"
  },
  {
    "path": "src/utilities/mse.test.ts",
    "chars": 616,
    "preview": "import { toArray } from './to-array';\nimport { zeros } from './zeros';\n\ndescribe('mse', () => {\n  test('should return th"
  },
  {
    "path": "src/utilities/mse.ts",
    "chars": 198,
    "preview": "export function mse(errors: Float32Array): number {\n  // mean squared error\n  let sum = 0;\n  for (let i = 0; i < errors."
  },
  {
    "path": "src/utilities/ones.test.ts",
    "chars": 421,
    "preview": "import { ones, ones2D } from './ones';\n\ndescribe('ones', () => {\n  test('should return an array with all ones', () => {\n"
  },
  {
    "path": "src/utilities/ones.ts",
    "chars": 292,
    "preview": "export function ones(size: number): Float32Array {\n  return new Float32Array(size).fill(1);\n}\n\nexport function ones2D(wi"
  },
  {
    "path": "src/utilities/random-weight.test.ts",
    "chars": 167,
    "preview": "import { randomWeight } from './random-weight';\n\ndescribe('randomWeight', () => {\n  test('weight', () => {\n    expect(ty"
  },
  {
    "path": "src/utilities/random-weight.ts",
    "chars": 79,
    "preview": "export function randomWeight(): number {\n  return Math.random() * 0.4 - 0.2;\n}\n"
  }
]

// ... and 20 more files (download for full content)

About this extraction

This page contains the full source code of the BrainJS/brain.js GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 220 files (944.6 KB), approximately 255.6k tokens, and a symbol index with 1061 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!