├── .babelrc
├── .github
└── workflows
│ ├── documentationjs.yml
│ ├── lactame.yml
│ ├── nodejs.yml
│ └── release.yml
├── .gitignore
├── .npmrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── dist
├── ml-regression.js
├── ml-regression.js.map
├── ml-regression.min.js
└── ml-regression.min.js.map
├── eslint.config.mjs
├── package.json
├── rollup.config.mjs
└── src
├── __tests__
├── 2d-polynomial-fit.test.js
├── kernel-ridge-regression.test.js
├── non-linear-regression.test.js
└── re-export.test.js
├── index.js
└── regression
├── kernel-ridge-regression.js
├── poly-fit-regression2d.js
└── potential-regression.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["@babel/plugin-transform-modules-commonjs"]
3 | }
4 |
--------------------------------------------------------------------------------
/.github/workflows/documentationjs.yml:
--------------------------------------------------------------------------------
1 | name: Deploy documentation.js on GitHub pages
2 |
3 | on:
4 | workflow_dispatch:
5 | release:
6 | types: [published]
7 |
8 | jobs:
9 | deploy:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v3
13 | - name: Build documentation
14 | uses: zakodium/documentationjs-action@v1
15 | - name: Deploy to GitHub pages
16 | uses: JamesIves/github-pages-deploy-action@releases/v4
17 | with:
18 | token: ${{ secrets.BOT_TOKEN }}
19 | branch: gh-pages
20 | folder: docs
21 | clean: true
22 |
--------------------------------------------------------------------------------
/.github/workflows/lactame.yml:
--------------------------------------------------------------------------------
1 | name: Deploy build on lactame.com
2 |
3 | on:
4 | release:
5 | types: [published]
6 |
7 | env:
8 | NODE_VERSION: 22.x
9 |
10 | jobs:
11 | deploy:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v4
15 | - name: Get package name
16 | run: echo "PACKAGE_NAME=$(jq .name package.json | tr -d '"')" >> $GITHUB_ENV
17 | - uses: actions/setup-node@v4
18 | with:
19 | node-version: ${{ env.NODE_VERSION }}
20 | - name: Install dependencies
21 | run: npm install
22 | - name: Build project
23 | run: npm run build
24 | - name: Deploy to lactame.com
25 | uses: zakodium/lactame-action@v1
26 | with:
27 | token: ${{ secrets.LACTAME_TOKEN }}
28 | name: ${{ env.PACKAGE_NAME }}
29 | folder: dist
30 |
--------------------------------------------------------------------------------
/.github/workflows/nodejs.yml:
--------------------------------------------------------------------------------
1 | name: Node.js CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 |
9 | jobs:
10 | nodejs:
11 | # Documentation: https://github.com/zakodium/workflows#nodejs-ci
12 | uses: zakodium/workflows/.github/workflows/nodejs.yml@nodejs-v1
13 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | release:
10 | # Documentation: https://github.com/zakodium/workflows#release
11 | uses: zakodium/workflows/.github/workflows/release.yml@release-v1
12 | with:
13 | npm: true
14 | secrets:
15 | github-token: ${{ secrets.BOT_TOKEN }}
16 | npm-token: ${{ secrets.NPM_BOT_TOKEN }}
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 | lib
40 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # [5.0.0](https://github.com/mljs/regression/compare/v4.4.2...v5.0.0) (2019-06-29)
2 |
3 |
4 | ### chore
5 |
6 | * update dependencies and remove support for Node.js 6 ([e1296b1](https://github.com/mljs/regression/commit/e1296b1))
7 |
8 |
9 | ### BREAKING CHANGES
10 |
11 | * Node.js 6 is no longer supported
12 |
13 |
14 |
15 |
16 | ## [6.3.0](https://github.com/mljs/regression/compare/v6.2.0...v6.3.0) (2025-05-16)
17 |
18 |
19 | ### Features
20 |
21 | * add npm on lactame CDN ([61ded60](https://github.com/mljs/regression/commit/61ded606af552c263b2bc553c0401e9ccac7046c))
22 |
23 | ## [6.2.0](https://github.com/mljs/regression/compare/v6.1.0...v6.2.0) (2024-10-17)
24 |
25 |
26 | ### Features
27 |
28 | * update dependencies ([a498858](https://github.com/mljs/regression/commit/a498858f68b0f900fdfec1de666838f45bdc0be8))
29 |
30 |
31 | ### Bug Fixes
32 |
33 | * update robust-polynomial to avoid multiple import of regression-base ([6a8853d](https://github.com/mljs/regression/commit/6a8853df1fe621ee1f476aeff02d1d487bc12d89))
34 |
35 | ## [6.1.0](https://github.com/mljs/regression/compare/v6.0.0...v6.1.0) (2024-05-03)
36 |
37 |
38 | ### Features
39 |
40 | * throw if polynomial regression 2d can not be done because not enough points ([5784863](https://github.com/mljs/regression/commit/57848639c5f608dbe91868953209cf0578400a3d))
41 |
42 |
43 | ### Bug Fixes
44 |
45 | * add CI ([a915de0](https://github.com/mljs/regression/commit/a915de020998faa5c9dfb54c47802e3fb9a3de93))
46 |
47 | ## [6.0.0](https://github.com/mljs/regression/compare/v5.0.1...v6.0.0) (2024-05-03)
48 |
49 |
50 | ### ⚠ BREAKING CHANGES
51 |
52 | * throw error if insufficient number of points
53 |
54 | ### Features
55 |
56 | * throw error if insufficient number of points ([00eb422](https://github.com/mljs/regression/commit/00eb422f371914643700a0af0efd8ed9c7ef679b))
57 |
58 | ## [4.4.2](https://github.com/mljs/regression/compare/v4.4.1...v4.4.2) (2017-07-21)
59 |
60 |
61 |
62 |
63 | ## [4.4.1](https://github.com/mljs/regression/compare/v4.4.0...v4.4.1) (2017-06-29)
64 |
65 |
66 |
67 |
68 | # [4.4.0](https://github.com/mljs/regression/compare/v4.3.0...v4.4.0) (2017-06-28)
69 |
70 |
71 | ### Bug Fixes
72 |
73 | * do not use '.default' in index ([b8e2934](https://github.com/mljs/regression/commit/b8e2934))
74 | * export modules as default ([#27](https://github.com/mljs/regression/issues/27)) ([072df5a](https://github.com/mljs/regression/commit/072df5a))
75 |
76 |
77 | ### Features
78 |
79 | * add MultivariateLinearRegression ([152478f](https://github.com/mljs/regression/commit/152478f))
80 |
81 |
82 |
83 |
84 | # [4.3.0](https://github.com/mljs/regression/compare/v4.2.1...v4.3.0) (2017-06-22)
85 |
86 |
87 | ### Features
88 |
89 | * add RobustPolynomialRegression ([dffb44c](https://github.com/mljs/regression/commit/dffb44c))
90 |
91 |
92 |
93 |
94 | ## [4.2.1](https://github.com/mljs/regression/compare/v4.2.0...v4.2.1) (2017-04-25)
95 |
96 |
97 |
98 |
99 | # [4.2.0](https://github.com/mljs/regression/compare/v4.1.1...v4.2.0) (2016-10-21)
100 |
101 |
102 |
103 |
104 | ## [4.1.1](https://github.com/mljs/regression/compare/v4.1.0...v4.1.1) (2016-09-21)
105 |
106 |
107 | ### Bug Fixes
108 |
109 | * increase test coverage ([#23](https://github.com/mljs/regression/issues/23)) ([1c806c5](https://github.com/mljs/regression/commit/1c806c5))
110 |
111 |
112 |
113 |
114 | # [4.1.0](https://github.com/mljs/regression/compare/v4.0.1...v4.1.0) (2016-09-21)
115 |
116 |
117 | ### Features
118 |
119 | * **theil-sen-regression:** add Theil-Sen regression and his test ([69517b2](https://github.com/mljs/regression/commit/69517b2))
120 |
121 |
122 |
123 |
124 | ## [4.0.1](https://github.com/mljs/regression/compare/v2.0.0...v4.0.1) (2016-08-16)
125 |
126 |
127 | ### Features
128 |
129 | * export and load implemented. ([ac42974](https://github.com/mljs/regression/commit/ac42974))
130 | * fit implemented. ([dd31444](https://github.com/mljs/regression/commit/dd31444))
131 | * predict implemented. ([4632fb7](https://github.com/mljs/regression/commit/4632fb7))
132 |
133 |
134 |
135 | 1.1.1 / 2015-11-21
136 | ==================
137 |
138 | * fix(KRR): prediction function is now working
139 |
140 | 1.1.0 / 2015-11-19
141 | ==================
142 |
143 | * Add Kernel Ridge Regression
144 | * SLR: add toString method
145 | * SLR: add computeX method
146 |
147 | 1.0.0 / 2015-09-07
148 | ==================
149 |
150 | * first release
151 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 ml.js
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ml-regression
2 |
3 | [![NPM version][npm-image]][npm-url]
4 | [![build status][travis-image]][travis-url]
5 | [![npm download][download-image]][download-url]
6 |
7 | Regression algorithms.
8 |
9 | ## Installation
10 |
11 | `$ npm install ml-regression`
12 |
13 | ## Examples
14 |
15 | ### Simple linear regression
16 |
17 | ```js
18 | const SLR = require("ml-regression").SLR;
19 | let inputs = [80, 60, 10, 20, 30];
20 | let outputs = [20, 40, 30, 50, 60];
21 |
22 | let regression = new SLR(inputs, outputs);
23 | regression.toString(3) === "f(x) = - 0.265 * x + 50.6";
24 | ```
25 |
26 | #### External links
27 |
28 | Check this cool blog post for a detailed example:
29 | https://hackernoon.com/machine-learning-with-javascript-part-1-9b97f3ed4fe5
30 |
31 | ### Polynomial regression
32 |
33 | ```js
34 | const PolynomialRegression = require("ml-regression").PolynomialRegression;
35 | const x = [50, 50, 50, 70, 70, 70, 80, 80, 80, 90, 90, 90, 100, 100, 100];
36 | const y = [
37 | 3.3, 2.8, 2.9, 2.3, 2.6, 2.1, 2.5, 2.9, 2.4, 3.0, 3.1, 2.8, 3.3, 3.5, 3.0,
38 | ];
39 | const degree = 5; // setup the maximum degree of the polynomial
40 | const regression = new PolynomialRegression(x, y, degree);
41 | console.log(regression.predict(80)); // Apply the model to some x value. Prints 2.6.
42 | console.log(regression.coefficients); // Prints the coefficients in increasing order of power (from 0 to degree).
43 | console.log(regression.toString(3)); // Prints a human-readable version of the function.
44 | console.log(regression.toLaTeX());
45 | ```
46 |
47 | ## License
48 |
49 | [MIT](./LICENSE)
50 |
51 | [npm-image]: https://img.shields.io/npm/v/ml-regression.svg?style=flat-square
52 | [npm-url]: https://npmjs.org/package/ml-regression
53 | [travis-image]: https://img.shields.io/travis/mljs/regression/main.svg?style=flat-square
54 | [travis-url]: https://travis-ci.org/mljs/regression
55 | [download-image]: https://img.shields.io/npm/dm/ml-regression.svg?style=flat-square
56 | [download-url]: https://npmjs.org/package/ml-regression
57 |
--------------------------------------------------------------------------------
/dist/ml-regression.min.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).Regression={})}(this,(function(t){"use strict";const e=Object.prototype.toString;function r(t){const r=e.call(t);return r.endsWith("Array]")&&!r.includes("Big")}var s=Object.freeze({__proto__:null,isAnyArray:r});function o(t,e){if(!r(t)||!r(e))throw new TypeError("x and y must be arrays");if(t.length!==e.length)throw new RangeError("x and y arrays must have the same length")}class i{constructor(){if(new.target===i)throw new Error("BaseRegression must be subclassed")}predict(t){if("number"==typeof t)return this._predict(t);if(r(t)){const e=[];for(const r of t)e.push(this._predict(r));return e}throw new TypeError("x must be a number or array")}_predict(t){throw new Error("_predict must be implemented")}train(){}toString(t){return""}toLaTeX(t){return""}score(t,e){o(t,e);const r=t.length,s=new Array(r);for(let e=0;e1&&void 0!==arguments[1]?arguments[1]:{};if(!r(t))throw new TypeError("input must be an array");if(0===t.length)throw new TypeError("input must not be empty");if(void 0!==s.output){if(!r(s.output))throw new TypeError("output option must be an array if specified");e=s.output}else e=new Array(t.length);var o=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!r(t))throw new TypeError("input must be an array");if(0===t.length)throw new TypeError("input must not be empty");var s=e.fromIndex,o=void 0===s?0:s,i=e.toIndex,n=void 0===i?t.length:i;if(o<0||o>=t.length||!Number.isInteger(o))throw new Error("fromIndex must be a positive integer smaller than length");if(n<=o||n>t.length||!Number.isInteger(n))throw new Error("toIndex must be an integer greater than fromIndex and at most equal to length");for(var h=t[o],a=o+1;a1&&void 0!==arguments[1]?arguments[1]:{};if(!r(t))throw new TypeError("input must be an array");if(0===t.length)throw new TypeError("input must not be empty");var s=e.fromIndex,o=void 0===s?0:s,i=e.toIndex,n=void 0===i?t.length:i;if(o<0||o>=t.length||!Number.isInteger(o))throw new Error("fromIndex must be a positive integer smaller than length");if(n<=o||n>t.length||!Number.isInteger(n))throw new Error("toIndex must be an integer greater than fromIndex and at most equal to length");for(var h=t[o],a=o+1;ah&&(h=t[a]);return h}(t);if(o===i)throw new RangeError("minimum and maximum input values are equal. Cannot rescale a constant array");var n=s.min,h=void 0===n?s.autoMinMax?o:0:n,a=s.max,l=void 0===a?s.autoMinMax?i:1:a;if(h>=l)throw new RangeError("min option must be smaller than max option");for(var u=(l-h)/(i-o),c=0;c=0&&r?` ${M(t,e-1)}`:M(t,e)).padEnd(e)}function M(t,e){let r=t.toString();if(r.length<=e)return r;let s=t.toFixed(e);if(s.length>e&&(s=t.toFixed(Math.max(0,e-(s.length-e)))),s.length<=e&&!s.startsWith("0.000")&&!s.startsWith("-0.000"))return s;let o=t.toExponential(e);return o.length>e&&(o=t.toExponential(Math.max(0,e-(o.length-e)))),o.slice(0)}function b(t,e,r){let s=r?t.rows:t.rows-1;if(e<0||e>s)throw new RangeError("Row index out of range")}function x(t,e,r){let s=r?t.columns:t.columns-1;if(e<0||e>s)throw new RangeError("Column index out of range")}function v(t,e){if(e.to1DArray&&(e=e.to1DArray()),e.length!==t.columns)throw new RangeError("vector size must be the same as the number of columns");return e}function E(t,e){if(e.to1DArray&&(e=e.to1DArray()),e.length!==t.rows)throw new RangeError("vector size must be the same as the number of rows");return e}function S(t,e){if(!m.isAnyArray(e))throw new TypeError("row indices must be an array");for(let r=0;r=t.rows)throw new RangeError("row indices are out of range")}function R(t,e){if(!m.isAnyArray(e))throw new TypeError("column indices must be an array");for(let r=0;r=t.columns)throw new RangeError("column indices are out of range")}function A(t,e,r,s,o){if(5!==arguments.length)throw new RangeError("expected 4 arguments");if(k("startRow",e),k("endRow",r),k("startColumn",s),k("endColumn",o),e>r||s>o||e<0||e>=t.rows||r<0||r>=t.rows||s<0||s>=t.columns||o<0||o>=t.columns)throw new RangeError("Submatrix indices are out of range")}function T(t,e=0){let r=[];for(let s=0;s=o)throw new RangeError("min must be smaller than max");let n=o-s,h=new V(t,e);for(let r=0;rr?(o=!0,r=e):(s=!1,o=!0);t++}return s}isReducedEchelonForm(){let t=0,e=0,r=-1,s=!0,o=!1;for(;tr?(o=!0,r=e):(s=!1,o=!0);for(let r=e+1;rt.get(s,r)&&(s=o);if(0===t.get(s,r))r++;else{t.swapRows(e,s);let o=t.get(e,r);for(let s=r;s=0;)if(0===t.maxRow(s))s--;else{let o=0,i=!1;for(;ot[e]&&(t[e]=this.get(e,r));return t}case"column":{const t=new Array(this.columns).fill(Number.NEGATIVE_INFINITY);for(let e=0;et[r]&&(t[r]=this.get(e,r));return t}case void 0:{let t=this.get(0,0);for(let e=0;et&&(t=this.get(e,r));return t}default:throw new Error(`invalid option: ${t}`)}}maxIndex(){I(this);let t=this.get(0,0),e=[0,0];for(let r=0;rt&&(t=this.get(r,s),e[0]=r,e[1]=s);return e}min(t){if(this.isEmpty())return NaN;switch(t){case"row":{const t=new Array(this.rows).fill(Number.POSITIVE_INFINITY);for(let e=0;ee&&(e=this.get(t,r));return e}maxRowIndex(t){b(this,t),I(this);let e=this.get(t,0),r=[t,0];for(let s=1;se&&(e=this.get(t,s),r[1]=s);return r}minRow(t){if(b(this,t),this.isEmpty())return NaN;let e=this.get(t,0);for(let r=1;re&&(e=this.get(r,t));return e}maxColumnIndex(t){x(this,t),I(this);let e=this.get(0,t),r=[0,t];for(let s=1;se&&(e=this.get(s,t),r[0]=s);return r}minColumn(t){if(x(this,t),this.isEmpty())return NaN;let e=this.get(0,t);for(let r=1;r=1;s/=2)1&s&&(e=e.mmul(r)),r=r.mmul(r);return e}strassen2x2(t){t=V.checkMatrix(t);let e=new V(2,2);const r=this.get(0,0),s=t.get(0,0),o=this.get(0,1),i=t.get(0,1),n=this.get(1,0),h=t.get(1,0),a=this.get(1,1),l=t.get(1,1),u=(r+a)*(s+l),c=(n+a)*s,f=r*(i-l),m=a*(h-s),g=(r+o)*l,w=u+m-g+(o-a)*(h+l),p=f+g,d=c+m,y=u-c+f+(n-r)*(s+i);return e.set(0,0,w),e.set(0,1,p),e.set(1,0,d),e.set(1,1,y),e}strassen3x3(t){t=V.checkMatrix(t);let e=new V(3,3);const r=this.get(0,0),s=this.get(0,1),o=this.get(0,2),i=this.get(1,0),n=this.get(1,1),h=this.get(1,2),a=this.get(2,0),l=this.get(2,1),u=this.get(2,2),c=t.get(0,0),f=t.get(0,1),m=t.get(0,2),g=t.get(1,0),w=t.get(1,1),p=t.get(1,2),d=t.get(2,0),y=t.get(2,1),M=t.get(2,2),b=(r-i)*(-f+w),x=(-r+i+n)*(c-f+w),v=(i+n)*(-c+f),E=r*c,S=(-r+a+l)*(c-m+p),R=(-r+a)*(m-p),A=(a+l)*(-c+m),T=(-o+l+u)*(w+d-y),k=(o-u)*(w-y),I=o*d,N=(l+u)*(-d+y),$=(-o+n+h)*(p+d-M),q=(o-h)*(p-M),C=(n+h)*(-d+M),O=E+I+s*g,_=(r+s+o-i-n-l-u)*w+x+v+E+T+I+N,F=E+S+A+(r+s+o-n-h-a-l)*p+I+$+C,D=b+n*(-c+f+g-w-p-d+M)+x+E+I+$+q,j=b+x+v+E+h*y,L=I+$+q+C+i*m,z=E+S+R+l*(-c+m+g-w-p-d+y)+T+k+I,P=T+k+I+N+a*f,B=E+S+R+A+u*M;return e.set(0,0,O),e.set(0,1,_),e.set(0,2,F),e.set(1,0,D),e.set(1,1,j),e.set(1,2,L),e.set(2,0,z),e.set(2,1,P),e.set(2,2,B),e}mmulStrassen(t){t=V.checkMatrix(t);let e=this.clone(),r=e.rows,s=e.columns,o=t.rows,i=t.columns;function n(t,e,r){let s=t.rows,o=t.columns;if(s===e&&o===r)return t;{let s=N.zeros(e,r);return s=s.setSubMatrix(t,0,0),s}}s!==o&&console.warn(`Multiplying ${r} x ${s} and ${o} x ${i} matrix: dimensions do not match.`);let h=Math.max(r,o),a=Math.max(s,i);return e=n(e,h,a),function t(e,r,s,o){if(s<=512||o<=512)return e.mmul(r);s%2==1&&o%2==1?(e=n(e,s+1,o+1),r=n(r,s+1,o+1)):s%2==1?(e=n(e,s+1,o),r=n(r,s+1,o)):o%2==1&&(e=n(e,s,o+1),r=n(r,s,o+1));let i=parseInt(e.rows/2,10),h=parseInt(e.columns/2,10),a=e.subMatrix(0,i-1,0,h-1),l=r.subMatrix(0,i-1,0,h-1),u=e.subMatrix(0,i-1,h,e.columns-1),c=r.subMatrix(0,i-1,h,r.columns-1),f=e.subMatrix(i,e.rows-1,0,h-1),m=r.subMatrix(i,r.rows-1,0,h-1),g=e.subMatrix(i,e.rows-1,h,e.columns-1),w=r.subMatrix(i,r.rows-1,h,r.columns-1),p=t(N.add(a,g),N.add(l,w),i,h),d=t(N.add(f,g),l,i,h),y=t(a,N.sub(c,w),i,h),M=t(g,N.sub(m,l),i,h),b=t(N.add(a,u),w,i,h),x=t(N.sub(f,a),N.add(l,c),i,h),v=t(N.sub(u,g),N.add(m,w),i,h),E=N.add(p,M);E.sub(b),E.add(v);let S=N.add(y,b),R=N.add(d,M),A=N.sub(p,d);A.add(y),A.add(x);let T=N.zeros(2*E.rows,2*E.columns);return T=T.setSubMatrix(E,0,0),T=T.setSubMatrix(S,E.rows,0),T=T.setSubMatrix(R,0,E.columns),T=T.setSubMatrix(A,E.rows,E.columns),T.subMatrix(0,s-1,0,o-1)}(e,t=n(t,h,a),h,a)}scaleRows(t={}){if("object"!=typeof t)throw new TypeError("options must be an object");const{min:e=0,max:r=1}=t;if(!Number.isFinite(e))throw new TypeError("min must be a number");if(!Number.isFinite(r))throw new TypeError("max must be a number");if(e>=r)throw new RangeError("min must be smaller than max");let s=new V(this.rows,this.columns);for(let t=0;t0&&g(o,{min:e,max:r,output:o}),s.setRow(t,o)}return s}scaleColumns(t={}){if("object"!=typeof t)throw new TypeError("options must be an object");const{min:e=0,max:r=1}=t;if(!Number.isFinite(e))throw new TypeError("min must be a number");if(!Number.isFinite(r))throw new TypeError("max must be a number");if(e>=r)throw new RangeError("min must be smaller than max");let s=new V(this.rows,this.columns);for(let t=0;tr||e<0||e>=this.columns||r<0||r>=this.columns)throw new RangeError("Argument out of range");let s=new V(t.length,r-e+1);for(let o=0;o=this.rows)throw new RangeError(`Row index out of range: ${t[o]}`);s.set(o,i-e,this.get(t[o],i))}return s}subMatrixColumn(t,e,r){if(void 0===e&&(e=0),void 0===r&&(r=this.rows-1),e>r||e<0||e>=this.rows||r<0||r>=this.rows)throw new RangeError("Argument out of range");let s=new V(r-e+1,t.length);for(let o=0;o=this.columns)throw new RangeError(`Column index out of range: ${t[o]}`);s.set(i-e,o,this.get(i,t[o]))}return s}setSubMatrix(t,e,r){if((t=V.checkMatrix(t)).isEmpty())return this;A(this,e,e+t.rows-1,r,r+t.columns-1);for(let s=0;s=0))throw new TypeError("nColumns must be a positive integer");for(let r=0;r=0)this.#t(e,r);else{if(!m.isAnyArray(e))throw new TypeError("First argument must be a positive number or an array");{const t=e;if("number"!=typeof(r=(e=t.length)?t[0].length:0))throw new TypeError("Data must be a 2D array with at least one element");this.data=[];for(let s=0;s"number"==typeof t)))throw new TypeError("Input data contains non-numeric values");this.data.push(Float64Array.from(t[s]))}this.rows=e,this.columns=r}}}set(t,e,r){return this.data[t][e]=r,this}get(t,e){return this.data[t][e]}removeRow(t){return b(this,t),this.data.splice(t,1),this.rows-=1,this}addRow(t,e){return void 0===e&&(e=t,t=this.rows),b(this,t,!0),e=Float64Array.from(v(this,e)),this.data.splice(t,0,e),this.rows+=1,this}removeColumn(t){x(this,t);for(let e=0;e>t);return this},t.prototype.signPropagatingRightShiftM=function(t){if(t=e.checkMatrix(t),this.rows!==t.rows||this.columns!==t.columns)throw new RangeError("Matrices dimensions must be equal");for(let e=0;e>t.get(e,r));return this},t.signPropagatingRightShift=function(t,r){return new e(t).signPropagatingRightShift(r)},t.prototype.rightShift=function(t){return"number"==typeof t?this.rightShiftS(t):this.rightShiftM(t)},t.prototype.rightShiftS=function(t){for(let e=0;e>>t);return this},t.prototype.rightShiftM=function(t){if(t=e.checkMatrix(t),this.rows!==t.rows||this.columns!==t.columns)throw new RangeError("Matrices dimensions must be equal");for(let e=0;e>>t.get(e,r));return this},t.rightShift=function(t,r){return new e(t).rightShift(r)},t.prototype.zeroFillRightShift=t.prototype.rightShift,t.prototype.zeroFillRightShiftS=t.prototype.rightShiftS,t.prototype.zeroFillRightShiftM=t.prototype.rightShiftM,t.zeroFillRightShift=t.rightShift,t.prototype.not=function(){for(let t=0;t=0)this.#e=new V(t,t);else if(this.#e=new V(t),!this.isSymmetric())throw new TypeError("not symmetric data")}clone(){const t=new q(this.diagonalSize);for(const[e,r,s]of this.upperRightEntries())t.set(e,r,s);return t}toMatrix(){return new V(this)}get(t,e){return this.#e.get(t,e)}set(t,e,r){return this.#e.set(t,e,r),this.#e.set(e,t,r),this}removeCross(t){return this.#e.removeRow(t),this.#e.removeColumn(t),this}addCross(t,e){void 0===e&&(e=t,t=this.diagonalSize);const r=e.slice();return r.splice(t,1),this.#e.addRow(t,r),this.#e.addColumn(t,e),this}applyMask(t){if(t.length!==this.diagonalSize)throw new RangeError("Mask size do not match with matrix size");const e=[];for(const[r,s]of t.entries())s||e.push(r);e.reverse();for(const t of e)this.removeCross(t);return this}toCompact(){const{diagonalSize:t}=this,e=new Array(t*(t+1)/2);for(let r=0,s=0,o=0;o=t&&(r=++s);return e}static fromCompact(t){const e=t.length,r=(Math.sqrt(8*e+1)-1)/2;if(!Number.isInteger(r))throw new TypeError(`This array is not a compact representation of a Symmetric Matrix, ${JSON.stringify(t)}`);const s=new q(r);for(let o=0,i=0,n=0;n=r&&(o=++i);return s}*upperRightEntries(){for(let t=0,e=0;t=this.diagonalSize&&(e=++t)}}*upperRightValues(){for(let t=0,e=0;t=this.diagonalSize&&(e=++t)}}}q.prototype.klassType="SymmetricMatrix";class C extends q{static isDistanceMatrix(t){return q.isSymmetricMatrix(t)&&"DistanceMatrix"===t.klassSubType}constructor(t){if(super(t),!this.isDistance())throw new TypeError("Provided arguments do no produce a distance matrix")}set(t,e,r){return t===e&&(r=0),super.set(t,e,r)}addCross(t,e){return void 0===e&&(e=t,t=this.diagonalSize),(e=e.slice())[t]=0,super.addCross(t,e)}toSymmetricMatrix(){return new q(this)}clone(){const t=new C(this.diagonalSize);for(const[e,r,s]of this.upperRightEntries())e!==r&&t.set(e,r,s);return t}toCompact(){const{diagonalSize:t}=this,e=new Array((t-1)*t/2);for(let r=1,s=0,o=0;o=t&&(r=1+ ++s);return e}static fromCompact(t){const e=t.length;if(0===e)return new this(0);const r=(Math.sqrt(8*e+1)+1)/2;if(!Number.isInteger(r))throw new TypeError(`This array is not a compact representation of a DistanceMatrix, ${JSON.stringify(t)}`);const s=new this(r);for(let o=1,i=0,n=0;n=r&&(o=1+ ++i);return s}}C.prototype.klassSubType="DistanceMatrix";class O extends N{constructor(t,e,r){super(),this.matrix=t,this.rows=e,this.columns=r}}class _ extends O{constructor(t,e,r){S(t,e),R(t,r),super(t,e.length,r.length),this.rowIndices=e,this.columnIndices=r}set(t,e,r){return this.matrix.set(this.rowIndices[t],this.columnIndices[e],r),this}get(t,e){return this.matrix.get(this.rowIndices[t],this.columnIndices[e])}}class F extends N{constructor(t,e={}){const{rows:r=1}=e;if(t.length%r!==0)throw new Error("the data length is not divisible by the number of rows");super(),this.rows=r,this.columns=t.length/r,this.data=t}set(t,e,r){let s=this._calculateIndex(t,e);return this.data[s]=r,this}get(t,e){let r=this._calculateIndex(t,e);return this.data[r]}_calculateIndex(t,e){return t*this.columns+e}}class D extends N{constructor(t){super(),this.data=t,this.rows=t.length,this.columns=t[0].length}set(t,e,r){return this.data[t][e]=r,this}get(t,e){return this.data[t][e]}}class j{constructor(t){let e,r,s,o,i,n,h,a,l,u=(t=D.checkMatrix(t)).clone(),c=u.rows,f=u.columns,m=new Float64Array(c),g=1;for(e=0;eMath.abs(a[o])&&(o=e);if(o!==r){for(s=0;s=0;o--){for(s=0;se?s.set(o,e,t.get(o,e)):o===e?s.set(o,e,1):s.set(o,e,0);return s}get upperTriangularMatrix(){let t=this.LU,e=t.rows,r=t.columns,s=new V(e,r);for(let o=0;oMath.abs(e)?(r=e/t,Math.abs(t)*Math.sqrt(1+r*r)):0!==e?(r=t/e,Math.abs(e)*Math.sqrt(1+r*r)):0}class z{constructor(t){let e,r,s,o,i=(t=D.checkMatrix(t)).clone(),n=t.rows,h=t.columns,a=new Float64Array(h);for(s=0;s=0;i--){for(o=0;o=0;r--){for(t=0;t=0;t--)if(0!==m[t]){for(let e=t+1;e=0;t--){if(t0;){let t,e;for(t=v-2;t>=-1&&-1!==t;t--){const e=Number.MIN_VALUE+S*Math.abs(m[t]+Math.abs(m[t+1]));if(Math.abs(p[t])<=e||Number.isNaN(p[t])){p[t]=0;break}}if(t===v-2)e=4;else{let r;for(r=v-1;r>=t&&r!==t;r--){let e=(r!==v?Math.abs(p[r]):0)+(r!==t+1?Math.abs(p[r-1]):0);if(Math.abs(m[r])<=S*e){m[r]=0;break}}r===t?e=3:r===v-1?e=1:(e=2,t=r)}switch(t++,e){case 1:{let e=p[v-2];p[v-2]=0;for(let r=v-2;r>=t;r--){let o=L(m[r],e),i=m[r]/o,n=e/o;if(m[r]=o,r!==t&&(e=-n*p[r-1],p[r-1]=i*p[r-1]),l)for(let t=0;t=m[t+1]);){let e=m[t];if(m[t]=m[t+1],m[t+1]=e,l&&te&&o.set(i,r,t.get(i,r)/this.s[r]);let i=this.U,n=i.rows,h=i.columns,a=new V(r,n);for(let t=0;tt&&e++;return e}get diagonal(){return Array.from(this.s)}get threshold(){return Number.EPSILON/2*Math.max(this.m,this.n)*this.s[0]}get leftSingularVectors(){return this.U}get rightSingularVectors(){return this.V}get diagonalMatrix(){return V.diag(this.s)}}function B(t,e,r=!1){return t=D.checkMatrix(t),e=D.checkMatrix(e),r?new P(t).solve(e):t.isSquare()?new j(t).solve(e):new z(t).solve(e)}function X(t,e){let r=[];for(let s=0;so)return new Array(e.rows+1).fill(0);{let t=e.addRow(r,[0]);for(let e=0;e0;h--){for(c=0,n=0,l=0;l0&&(i=-i),e[h]=c*i,n-=o*i,r[h-1]=o-i,a=0;al)do{for(o=r[l],c=(r[l+1]-o)/(2*e[l]),f=L(c,1),c<0&&(f=-f),r[l]=e[l]/(c+f),r[l+1]=e[l]*(c+f),m=r[l+1],i=o-r[l],n=l+2;n=l;n--)for(p=w,w=g,M=y,o=g*e[n],i=g*c,f=L(c,e[n]),e[n+1]=y*f,y=e[n]/f,g=c/f,c=g*r[n]-y*o,r[n+1]=i+y*(g*o+y*r[n]),a=0;av*x);r[l]=r[l]+b,e[l]=0}for(n=0;n=l;h--)r[h]=e.get(h,l-1)/u,n+=r[h]*r[h];for(i=Math.sqrt(n),r[l]>0&&(i=-i),n-=r[l]*i,r[l]=r[l]-i,a=l;a=l;h--)o+=r[h]*e.get(h,a);for(o/=n,h=l;h<=f;h++)e.set(h,a,e.get(h,a)-o*r[h])}for(h=0;h<=f;h++){for(o=0,a=f;a>=l;a--)o+=r[a]*e.get(h,a);for(o/=n,a=l;a<=f;a++)e.set(h,a,e.get(h,a)-o*r[a])}r[l]=u*r[l],e.set(l,l-1,u*i)}}for(h=0;h=c+1;l--)if(0!==e.get(l,l-1)){for(h=l+1;h<=f;h++)r[h]=e.get(h,l-1);for(a=l;a<=f;a++){for(i=0,h=l;h<=f;h++)i+=r[h]*s.get(h,a);for(i=i/r[l]/e.get(l,l-1),h=l;h<=f;h++)s.set(h,a,s.get(h,a)+i*r[h])}}}(i,t,e,n),function(t,e,r,s,o){let i,n,h,a,l,u,c,f,m,g,w,p,d,y,M,b=t-1,x=0,v=t-1,E=Number.EPSILON,S=0,R=0,A=0,T=0,k=0,I=0,N=0,$=0;for(i=0;iv)&&(r[i]=o.get(i,i),e[i]=0),n=Math.max(i-1,0);n=x;){for(a=b;a>x&&(I=Math.abs(o.get(a-1,a-1))+Math.abs(o.get(a,a)),0===I&&(I=R),!(Math.abs(o.get(a,a-1))=0){for(N=A>=0?A+N:A-N,r[b-1]=f+N,r[b]=r[b-1],0!==N&&(r[b]=f-c/N),e[b-1]=0,e[b]=0,f=o.get(b,b-1),I=Math.abs(f)+Math.abs(N),A=f/I,T=N/I,k=Math.sqrt(A*A+T*T),A/=k,T/=k,n=b-1;n0)){for(I=Math.sqrt(I),m=a&&(N=o.get(l,l),k=f-N,I=m-N,A=(k*I-c)/o.get(l+1,l)+o.get(l,l+1),T=o.get(l+1,l+1)-N-k-I,k=o.get(l+2,l+1),I=Math.abs(A)+Math.abs(T)+Math.abs(k),A/=I,T/=I,k/=I,l!==a)&&!(Math.abs(o.get(l,l-1))*(Math.abs(T)+Math.abs(k))l+2&&o.set(i,i-3,0);for(h=l;h<=b-1&&(y=h!==b-1,h!==l&&(A=o.get(h,h-1),T=o.get(h+1,h-1),k=y?o.get(h+2,h-1):0,f=Math.abs(A)+Math.abs(T)+Math.abs(k),0!==f&&(A/=f,T/=f,k/=f)),0!==f);h++)if(I=Math.sqrt(A*A+T*T+k*k),A<0&&(I=-I),0!==I){for(h!==l?o.set(h,h-1,-I*f):a!==l&&o.set(h,h-1,-o.get(h,h-1)),A+=I,f=A/I,m=T/I,N=k/I,T/=A,k/=A,n=h;n=0;b--)if(A=r[b],T=e[b],0===T)for(a=b,o.set(b,b,1),i=b-1;i>=0;i--){for(c=o.get(i,i)-A,k=0,n=a;n<=b;n++)k+=o.get(i,n)*o.get(n,b);if(e[i]<0)N=c,I=k;else if(a=i,0===e[i]?o.set(i,b,0!==c?-k/c:-k/(E*R)):(f=o.get(i,i+1),m=o.get(i+1,i),T=(r[i]-A)*(r[i]-A)+e[i]*e[i],u=(f*I-N*k)/T,o.set(i,b,u),o.set(i+1,b,Math.abs(f)>Math.abs(N)?(-k-c*u)/f:(-I-m*u)/N)),u=Math.abs(o.get(i,b)),E*u*u>1)for(n=i;n<=b;n++)o.set(n,b,o.get(n,b)/u)}else if(T<0)for(a=b-1,Math.abs(o.get(b,b-1))>Math.abs(o.get(b-1,b))?(o.set(b-1,b-1,T/o.get(b,b-1)),o.set(b-1,b,-(o.get(b,b)-A)/o.get(b,b-1))):(M=Q(0,-o.get(b-1,b),o.get(b-1,b-1)-A,T),o.set(b-1,b-1,M[0]),o.set(b-1,b,M[1])),o.set(b,b-1,0),o.set(b,b,1),i=b-2;i>=0;i--){for(g=0,w=0,n=a;n<=b;n++)g+=o.get(i,n)*o.get(n,b-1),w+=o.get(i,n)*o.get(n,b);if(c=o.get(i,i)-A,e[i]<0)N=c,k=g,I=w;else if(a=i,0===e[i]?(M=Q(-g,-w,c,T),o.set(i,b-1,M[0]),o.set(i,b,M[1])):(f=o.get(i,i+1),m=o.get(i+1,i),p=(r[i]-A)*(r[i]-A)+e[i]*e[i]-T*T,d=2*(r[i]-A)*T,0===p&&0===d&&(p=E*R*(Math.abs(c)+Math.abs(T)+Math.abs(f)+Math.abs(m)+Math.abs(N))),M=Q(f*k-N*g+T*w,f*I-N*w-T*g,p,d),o.set(i,b-1,M[0]),o.set(i,b,M[1]),Math.abs(f)>Math.abs(N)+Math.abs(T)?(o.set(i+1,b-1,(-g-c*o.get(i,b-1)+T*o.get(i,b))/f),o.set(i+1,b,(-w-c*o.get(i,b)-T*o.get(i,b-1))/f)):(M=Q(-k-m*o.get(i,b-1),-I-m*o.get(i,b),N,T),o.set(i+1,b-1,M[0]),o.set(i+1,b,M[1]))),u=Math.max(Math.abs(o.get(i,b-1)),Math.abs(o.get(i,b))),E*u*u>1)for(n=i;n<=b;n++)o.set(n,b-1,o.get(n,b-1)/u),o.set(n,b,o.get(n,b)/u)}for(i=0;iv)for(n=i;n=x;n--)for(i=x;i<=v;i++){for(N=0,h=x;h<=Math.min(n,v);h++)N+=s.get(i,h)*o.get(h,n);s.set(i,n,N)}}(i,a,h,n,t)}this.n=i,this.e=a,this.d=h,this.V=n}get realEigenvalues(){return Array.from(this.d)}get imaginaryEigenvalues(){return Array.from(this.e)}get eigenvectorMatrix(){return this.V}get diagonalMatrix(){let t,e,r=this.n,s=this.e,o=this.d,i=new V(r,r);for(t=0;t0?i.set(t,t+1,s[t]):s[t]<0&&i.set(t,t-1,s[t])}return i}}function Q(t,e,r,s){let o,i;return Math.abs(r)>Math.abs(s)?(o=s/r,i=r+o*s,[(t+o*e)/i,(e-o*t)/i]):(o=r/s,i=s+o*r,[(o*t+e)/i,(o*e-t)/i])}class W{constructor(t){if(!(t=D.checkMatrix(t)).isSymmetric())throw new Error("Matrix is not symmetric");let e,r,s,o=t,i=o.rows,n=new V(i,i),h=!0;for(r=0;r0,n.set(r,r,Math.sqrt(Math.max(t,0))),s=r+1;s=0;i--)for(o=0;oi;e++)l=t.transpose().mmul(n).div(n.transpose().mmul(n).get(0,0)),l=l.div(l.norm()),h=t.mmul(l).div(l.transpose().mmul(l).get(0,0)),e>0&&(c=h.clone().sub(u).pow(2).sum()),u=h.clone(),r?(a=r.transpose().mmul(h).div(h.transpose().mmul(h).get(0,0)),a=a.div(a.norm()),n=r.mmul(a).div(a.transpose().mmul(a).get(0,0))):n=h;if(r){let e=t.transpose().mmul(h).div(h.transpose().mmul(h).get(0,0));e=e.div(e.norm());let s=t.clone().sub(h.clone().mmul(e.transpose())),o=n.transpose().mmul(h).div(h.transpose().mmul(h).get(0,0)),i=r.clone().sub(h.clone().mulS(o.get(0,0)).mmul(a.transpose()));this.t=h,this.p=e.transpose(),this.w=l.transpose(),this.q=a,this.u=n,this.s=h.transpose().mmul(h),this.xResidual=s,this.yResidual=i,this.betas=o}else this.w=l.transpose(),this.s=h.transpose().mmul(h).sqrt(),this.t=s?h.clone().div(this.s.get(0,0)):h,this.xResidual=t.sub(h.mmul(l.transpose()))}}l.AbstractMatrix=N,l.CHO=W,l.CholeskyDecomposition=W,l.DistanceMatrix=C,l.EVD=J,l.EigenvalueDecomposition=J,l.LU=j,l.LuDecomposition=j;var K=l.Matrix=V;l.MatrixColumnSelectionView=class extends O{constructor(t,e){R(t,e),super(t,t.rows,e.length),this.columnIndices=e}set(t,e,r){return this.matrix.set(t,this.columnIndices[e],r),this}get(t,e){return this.matrix.get(t,this.columnIndices[e])}},l.MatrixColumnView=class extends O{constructor(t,e){x(t,e),super(t,t.rows,1),this.column=e}set(t,e,r){return this.matrix.set(t,this.column,r),this}get(t){return this.matrix.get(t,this.column)}},l.MatrixFlipColumnView=class extends O{constructor(t){super(t,t.rows,t.columns)}set(t,e,r){return this.matrix.set(t,this.columns-e-1,r),this}get(t,e){return this.matrix.get(t,this.columns-e-1)}},l.MatrixFlipRowView=class extends O{constructor(t){super(t,t.rows,t.columns)}set(t,e,r){return this.matrix.set(this.rows-t-1,e,r),this}get(t,e){return this.matrix.get(this.rows-t-1,e)}},l.MatrixRowSelectionView=class extends O{constructor(t,e){S(t,e),super(t,e.length,t.columns),this.rowIndices=e}set(t,e,r){return this.matrix.set(this.rowIndices[t],e,r),this}get(t,e){return this.matrix.get(this.rowIndices[t],e)}},l.MatrixRowView=class extends O{constructor(t,e){b(t,e),super(t,1,t.columns),this.row=e}set(t,e,r){return this.matrix.set(this.row,e,r),this}get(t,e){return this.matrix.get(this.row,e)}},l.MatrixSelectionView=_,l.MatrixSubView=class extends O{constructor(t,e,r,s,o){A(t,e,r,s,o),super(t,r-e+1,o-s+1),this.startRow=e,this.startColumn=s}set(t,e,r){return this.matrix.set(this.startRow+t,this.startColumn+e,r),this}get(t,e){return this.matrix.get(this.startRow+t,this.startColumn+e)}};var G=l.MatrixTransposeView=class extends O{constructor(t){super(t,t.columns,t.rows)}set(t,e,r){return this.matrix.set(e,t,r),this}get(t,e){return this.matrix.get(e,t)}};l.NIPALS=Y,l.Nipals=Y,l.QR=z,l.QrDecomposition=z;var H=l.SVD=P;l.SingularValueDecomposition=P,l.SymmetricMatrix=q,l.WrapperMatrix1D=F,l.WrapperMatrix2D=D,l.correlation=function(t,e=t,r={}){t=new V(t);let s=!1;if("object"!=typeof e||V.isMatrix(e)||m.isAnyArray(e)?e=new V(e):(r=e,e=t,s=!0),t.rows!==e.rows)throw new TypeError("Both matrices must have the same number of rows");const{center:o=!0,scale:i=!0}=r;o&&(t.center("column"),s||e.center("column")),i&&(t.scale("column"),s||e.scale("column"));const n=t.standardDeviation("column",{unbiased:!0}),h=s?n:e.standardDeviation("column",{unbiased:!0}),a=t.transpose().mmul(e);for(let e=0;ee?i[t]=1/i[t]:i[t]=0;return o.mmul(V.diag(i).mmul(s.transpose()))},et=l.solve=B;l.wrap=function(t,e){if(m.isAnyArray(t))return t[0]&&m.isAnyArray(t[0])?new D(t):new F(t,e);throw new Error("the argument is not an array")};const rt=K,st=G,ot=H;Z.Matrix&&Z.Matrix;const it=tt,nt=et;class ht extends i{constructor(t,e,r,s={}){if(super(),!0===t)this.degree=e.degree,this.powers=e.powers,this.coefficients=e.coefficients;else{o(t,e);const i=function(t,e,r,s={}){const o=t.length;let{interceptAtZero:i=!1}=s,n=[];if(Array.isArray(r))n=r,i=!1;else if("number"==typeof r)if(i){n=new Array(r);for(let t=0;t0&&e!==this.coefficients.length-1?h=` + ${h}`:e!==this.coefficients.length-1&&(h=` ${h}`)),i=h+i;return i.startsWith("+")&&(i=i.slice(1)),`f(x) = ${i}`}static load(t){if("polynomialRegression"!==t.name)throw new TypeError("not a polynomial regression model");return new ht(!0,t)}}class at extends i{constructor(t,e,r){if(super(),!0===t)this.A=e.A,this.M=e.M;else{if(t.length!==e.length)throw new RangeError("input and output array have a different length");let s=new ht(t,e,[r]);this.A=s.coefficients[0],this.M=r}}_predict(t){return this.A*t**this.M}toJSON(){return{name:"potentialRegression",A:this.A,M:this.M}}toString(t){return`f(x) = ${n(this.A,t)} * x^${this.M}`}toLaTeX(t){return this.M>=0?`f(x) = ${n(this.A,t)}x^{${this.M}}`:`f(x) = \\frac{${n(this.A,t)}}{x^{${-this.M}}}`}static load(t){if("potentialRegression"!==t.name)throw new TypeError("not a potential regression model");return new at(!0,t)}}class lt extends i{constructor(t,e){if(super(),!0===t){const t=e;this.slope=t.slope,this.intercept=t.intercept,this.coefficients=[t.intercept,t.slope]}else{o(t,e);const r=function(t,e){const r=t.length;let s=0,o=0,i=0,n=0;for(let h=0;h=0?`f(x) = ${n(this.B,t)}e^{${n(this.A,t)}x}`:`f(x) = \\frac{${n(this.B,t)}}{e^{${n(-this.A,t)}x}}`}static load(t){if("exponentialRegression"!==t.name)throw new TypeError("not a exponential regression model");return new ut(!0,t)}}class ct extends i{constructor(t,e){super(),!0===t?(this.A=e.A,this.B=e.B):(o(t,e),function(t,e,r){const s=e.length,o=new Array(s),i=new Array(s);for(let t=0;t=0?`f(x) = ${n(this.A,t)}x^{${n(this.B,t)}}`:`f(x) = \\frac{${n(this.A,t)}}{x^{${n(-this.B,t)}}}`,e=e.replace(/e([+-]?[0-9]+)/g,"e^{$1}"),e}static load(t){if("powerRegression"!==t.name)throw new TypeError("not a power regression model");return new ct(!0,t)}}class ft{constructor(t,e,r={}){const{intercept:s=!0,statistics:o=!0}=r;if(this.statistics=o,!0===t)this.weights=e.weights,this.inputs=e.inputs,this.outputs=e.outputs,this.intercept=e.intercept;else{t=new rt(t),e=new rt(e),s&&t.addColumn(new Array(t.rows).fill(1));let r=t.transpose();const i=r.mmul(t),n=r.mmul(e),h=new ot(i).inverse(),a=n.transpose().mmul(h).transpose();if(this.weights=a.to2DArray(),this.inputs=t.columns,this.outputs=e.columns,s&&this.inputs--,this.intercept=s,o){const r=t.mmul(a),s=e.clone().addM(r.neg()).to2DArray().map((t=>Math.pow(t[0],2))).reduce(((t,e)=>t+e))/(e.rows-t.columns);this.stdError=Math.sqrt(s),this.stdErrorMatrix=it(i).mul(s),this.stdErrors=this.stdErrorMatrix.diagonal().map((t=>Math.sqrt(t))),this.tStats=this.weights.map(((t,e)=>0===this.stdErrors[e]?0:t[0]/this.stdErrors[e]))}}}predict(t){if(Array.isArray(t)){if("number"==typeof t[0])return this._predict(t);if(Array.isArray(t[0])){const e=new Array(t.length);for(let r=0;r({label:e===this.weights.length-1?"Intercept":`X Variable ${e+1}`,coefficients:t,standardError:this.stdErrors[e],tStat:this.tStats[e]})))}:void 0}}static load(t){if("multivariateLinearRegression"!==t.name)throw new Error("not a MLR model");return new ft(!0,t)}}function mt(t,e){let r=0;for(let s=0;s{f.get(t,e)>=1e-15?f.set(t,e,1/f.get(t,e)):f.set(t,e,0)}));let m=rt.zeros(r,s);for(let t=0;tt[o]&&r(t,e,o),t[a];for(t[i=s(e,o)]>t[o]&&r(t,i,o),t[e]>t[o]&&r(t,e,o),t[i]>t[e]&&r(t,i,e),r(t,i,e+1),n=e+1,h=o;;){do{n++}while(t[e]>t[n]);do{h--}while(t[h]>t[e]);if(h=a&&(o=h-1)}}var r=function(t,e,r){var s;return s=[t[r],t[e]],t[e]=s[0],t[r]=s[1],s},s=function(t,e){return~~((t+e)/2)};t.exports?t.exports=e:window.median=e}()}(Yt);var Kt=h(Yt.exports);function Gt(t){if(!r(t))throw new TypeError("input must be an array");if(0===t.length)throw new TypeError("input must not be empty");return Kt(t.slice())}class Ht extends i{constructor(t,e){super(),!0===t?(this.slope=e.slope,this.intercept=e.intercept,this.coefficients=e.coefficients):(o(t,e),function(t,e,r){let s=e.length,o=new Array(s*s),i=0;for(let t=0;tt.residual-e.residual));const e=t.length,r=Math.floor(e/2);return e%2==0?t[r-1]:t[r]}function re(t,e,r,s){const o=new Array(s).fill(0).map(((t,e)=>e)),i=function(t,e,r){const s=Math.floor(t.length/r),o=new Array(s);for(let i=0;i0&&e!==a.length-1?h=` + ${h}`:e!==a.length-1&&(h=` ${h}`)),i=h+i;return i.startsWith("+")&&(i=i.slice(1)),`f(x) = ${i}`}static load(t){if("robustPolynomialRegression"!==t.name)throw new TypeError("not a RobustPolynomialRegression model");return new se(t,void 0,void 0)}}const oe={PotentialRegression:at};t.ExponentialRegression=ut,t.KRR=Ut,t.KernelRidgeRegression=Ut,t.MultivariateLinearRegression=ft,t.NLR=oe,t.NonLinearRegression=oe,t.PolinomialFitting2D=Qt,t.PolynomialRegression=ht,t.PowerRegression=ct,t.RobustPolynomialRegression=se,t.SLR=lt,t.SimpleLinearRegression=lt,t.TheilSenRegression=Ht}));
2 | //# sourceMappingURL=ml-regression.min.js.map
3 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import cheminfo from "eslint-config-cheminfo";
2 |
3 | export default [...cheminfo];
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ml-regression",
3 | "version": "6.3.0",
4 | "description": "Regression algorithms",
5 | "main": "lib/index.js",
6 | "module": "src/index.js",
7 | "files": [
8 | "lib",
9 | "src"
10 | ],
11 | "scripts": {
12 | "build": "cheminfo-build --entry src/index.js --root Regression",
13 | "compile": "rollup -c",
14 | "eslint": "eslint src",
15 | "eslint-fix": "npm run eslint -- --fix",
16 | "prepack": "npm run compile",
17 | "prettier": "prettier --check src",
18 | "prettier-write": "prettier --write src",
19 | "test": "npm run test-coverage && npm run eslint && npm run prettier",
20 | "test-only": "vitest run --coverage"
21 | },
22 | "repository": {
23 | "type": "git",
24 | "url": "https://github.com/mljs/regression.git"
25 | },
26 | "keywords": [
27 | "regression",
28 | "data",
29 | "mining",
30 | "datamining",
31 | "machine",
32 | "learning"
33 | ],
34 | "author": "Michaël Zasso",
35 | "license": "MIT",
36 | "bugs": {
37 | "url": "https://github.com/mljs/regression/issues"
38 | },
39 | "homepage": "https://github.com/mljs/regression",
40 | "devDependencies": {
41 | "@babel/plugin-transform-modules-commonjs": "^7.27.1",
42 | "@vitest/coverage-v8": "^3.1.3",
43 | "cheminfo-build": "^1.2.1",
44 | "eslint": "^9.26.0",
45 | "eslint-config-cheminfo": "^14.1.1",
46 | "globals": "^16.1.0",
47 | "prettier": "^3.5.3",
48 | "rollup": "^4.40.2",
49 | "vitest": "^3.1.3"
50 | },
51 | "dependencies": {
52 | "ml-kernel": "^3.0.0",
53 | "ml-matrix": "^6.12.1",
54 | "ml-regression-base": "^4.0.0",
55 | "ml-regression-exponential": "^3.0.2",
56 | "ml-regression-multivariate-linear": "^2.0.4",
57 | "ml-regression-polynomial": "^3.0.1",
58 | "ml-regression-power": "^3.0.0",
59 | "ml-regression-robust-polynomial": "^3.0.2",
60 | "ml-regression-simple-linear": "^3.0.1",
61 | "ml-regression-theil-sen": "^3.0.0"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | export default {
2 | input: 'src/index.js',
3 | output: {
4 | file: 'lib/index.js',
5 | format: 'cjs'
6 | }
7 | };
8 |
--------------------------------------------------------------------------------
/src/__tests__/2d-polynomial-fit.test.js:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from "vitest";
2 |
3 | import { PolinomialFitting2D as Polyfit } from "..";
4 |
5 | describe("2D polinomial fit", () => {
6 | const X = new Array(21);
7 | const y = new Array(21);
8 | for (let i = 0; i < 21; ++i) {
9 | X[i] = [i, i + 10];
10 | y[i] = i + 20;
11 | }
12 |
13 | const pf = new Polyfit(X, y, {
14 | order: 2,
15 | });
16 |
17 | it("Training coefficients", () => {
18 | const estimatedCoefficients = [
19 | 1.5587e1, 3.8873e-1, 5.2582e-3, 4.8498e-1, 2.1127e-3, -7.3709e-3,
20 | ];
21 | for (let i = 0; i < estimatedCoefficients.length; ++i) {
22 | expect(pf.coefficients.get(i, 0)).toBeCloseTo(
23 | estimatedCoefficients[i],
24 | 1e-2,
25 | );
26 | }
27 | });
28 |
29 | it("Prediction", () => {
30 | let test = new Array(11);
31 | let val = 0.5;
32 | for (let i = 0; i < 11; ++i) {
33 | test[i] = [val, val + 10];
34 | val++;
35 | }
36 |
37 | let y = pf.predict(test);
38 |
39 | let j = 0;
40 | for (let i = 20.5; i < 30.5; i++, j++) {
41 | expect(y[j]).toBeCloseTo(i, 1e-2);
42 | }
43 | });
44 |
45 | it("Other function test", () => {
46 | let testValues = [
47 | 15.041667, 9.375, 5.041667, 2.041667, 0.375, 0.041667, 1.041667, 3.375,
48 | 7.041667, 12.041667,
49 | ];
50 |
51 | let len = 21;
52 |
53 | let X = new Array(len);
54 | let val = 5;
55 | let y = new Array(len);
56 | for (let i = 0; i < len; ++i, val += 0.5) {
57 | X[i] = [val, val];
58 | y[i] = val * val + val * val;
59 | }
60 |
61 | let polyFit = new Polyfit(X, y, {
62 | order: 2,
63 | });
64 |
65 | let test = 10;
66 | let x1 = -4.75;
67 | let x2 = 4.75;
68 | let X1 = new Array(test);
69 | for (let i = 0; i < test; ++i) {
70 | X1[i] = [x1, x2];
71 | x1++;
72 | x2--;
73 | }
74 |
75 | let predict = polyFit.predict(X1);
76 | for (let i = 0; i < testValues.length; ++i) {
77 | expect(predict[i]).toBeCloseTo(testValues[i], 1e-2);
78 | }
79 | });
80 | it("must throw error", () => {
81 | const X = new Array(5);
82 | const y = new Array(5);
83 | for (let i = 0; i < 5; ++i) {
84 | X[i] = [i, i + 10];
85 | y[i] = i + 20;
86 | }
87 |
88 | expect(() => {
89 | const polyfit = new Polyfit(X, y, {
90 | order: 4,
91 | });
92 | return polyfit;
93 | }).toThrow("Insufficient number of points to create regression model.");
94 | });
95 | });
96 |
--------------------------------------------------------------------------------
/src/__tests__/kernel-ridge-regression.test.js:
--------------------------------------------------------------------------------
1 | import { Matrix } from "ml-matrix";
2 | import { describe, it, expect } from "vitest";
3 |
4 | import { KernelRidgeRegression } from "..";
5 |
6 | let nSamples = 10;
7 | let nVars = 2;
8 |
9 | let Xs = Matrix.random(nSamples, nVars);
10 | Xs.sub(0.5);
11 | let Ys = Matrix.zeros(nSamples, 1);
12 | for (let i = 0; i < nSamples; i++) {
13 | Ys.set(
14 | i,
15 | 0,
16 | Xs.get(i, 0) * Xs.get(i, 0) +
17 | 2 * Xs.get(i, 0) * Xs.get(i, 1) +
18 | Xs.get(i, 1) * Xs.get(i, 1),
19 | );
20 | }
21 |
22 | describe("Kernel ridge regression", () => {
23 | it("constant outputs", () => {
24 | let model = new KernelRidgeRegression(
25 | [
26 | [0, 0],
27 | [1, 1],
28 | ],
29 | [[0], [0]],
30 | );
31 | expect(
32 | model.predict([
33 | [1, 1],
34 | [2, 5],
35 | [4, 7],
36 | ]),
37 | ).toStrictEqual([[0], [0], [0]]);
38 | });
39 | it("Polynomial kernel should overfit the pattern", () => {
40 | let model = new KernelRidgeRegression(Xs, Ys, {
41 | kernelType: "polynomial",
42 | lambda: 0.0001,
43 | kernelOptions: { degree: 2, constant: 1 },
44 | });
45 | let Y = model.predict(Xs.to2DArray());
46 |
47 | for (let i = 0; i < Y.length; i++) {
48 | expect(Y[i][0]).toBeCloseTo(Ys.get(i, 0), 5e-3);
49 | }
50 | });
51 | it("Gaussian kernel should overfit the pattern", () => {
52 | let model = new KernelRidgeRegression(Xs, Ys, {
53 | kernelType: "gaussian",
54 | lambda: 0.0001,
55 | kernelOptions: { sigma: 0.1 },
56 | computeQuality: true,
57 | });
58 | let Y = model.predict(Xs.to2DArray());
59 | for (let i = 0; i < Y.length; i++) {
60 | expect(Y[i][0]).toBeCloseTo(Ys.get(i, 0), 5e-3);
61 | }
62 | });
63 | it("Load and export model", () => {
64 | let regression = new KernelRidgeRegression(true, {
65 | name: "kernelRidgeRegression",
66 | alpha: 1,
67 | inputs: 1,
68 | kernelType: "gaussian",
69 | kernelOptions: {},
70 | });
71 | expect(regression.alpha).toBe(1);
72 | expect(regression.kernelType).toBe("gaussian");
73 |
74 | let model = regression.toJSON();
75 | expect(model.name).toBe("kernelRidgeRegression");
76 | expect(model.alpha).toBe(1);
77 | expect(model.kernelType).toBe("gaussian");
78 | });
79 | });
80 |
--------------------------------------------------------------------------------
/src/__tests__/non-linear-regression.test.js:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from "vitest";
2 |
3 | import { NLR } from "..";
4 |
5 | describe("Non-linear regression", () => {
6 | describe("Should give the correct parameters ", () => {
7 | it("Potential regression", () => {
8 | let x = [0.2, 0.4, 0.6, 0.8, 1];
9 | let y = [0.196, 0.785, 1.7665, 3.1405, 4.9075];
10 | let result = new NLR.PotentialRegression(x, y, 2, {
11 | computeQuality: true,
12 | });
13 | expect(result.A).toBeCloseTo(4.9073, 10e-5);
14 | expect(result.M).toBe(2);
15 | const score = result.score(x, y);
16 | expect(score.r2).toBeGreaterThan(0.8);
17 | expect(score.chi2).toBeLessThan(0.1);
18 | expect(score.rmsd).toBeLessThan(0.01);
19 | expect(result.toString(4)).toBe("f(x) = 4.907 * x^2");
20 | expect(result.toLaTeX(4)).toBe("f(x) = 4.907x^{2}");
21 | });
22 | });
23 |
24 | describe("Load and export model ", () => {
25 | it("Potential regression", () => {
26 | let regression = NLR.PotentialRegression.load({
27 | name: "potentialRegression",
28 | A: 1,
29 | M: -1,
30 | });
31 | expect(regression.A).toBe(1);
32 | expect(regression.M).toBe(-1);
33 | expect(regression.toLaTeX()).toBe(String.raw`f(x) = \frac{1}{x^{1}}`);
34 |
35 | let model = regression.toJSON();
36 | expect(model.name).toBe("potentialRegression");
37 | expect(model.A).toBe(1);
38 | expect(model.M).toBe(-1);
39 | });
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/src/__tests__/re-export.test.js:
--------------------------------------------------------------------------------
1 | import { describe, it, expect } from "vitest";
2 |
3 | import * as regression from "..";
4 |
5 | describe("test that re-exports are OK", () => {
6 | it("should export functions", () => {
7 | expect(regression.SimpleLinearRegression).toBeInstanceOf(Function);
8 | expect(regression.PolynomialRegression).toBeInstanceOf(Function);
9 | expect(regression.ExponentialRegression).toBeInstanceOf(Function);
10 | expect(regression.PowerRegression).toBeInstanceOf(Function);
11 | expect(regression.MultivariateLinearRegression).toBeInstanceOf(Function);
12 | expect(regression.TheilSenRegression).toBeInstanceOf(Function);
13 | expect(regression.RobustPolynomialRegression).toBeInstanceOf(Function);
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import { PotentialRegression } from "./regression/potential-regression";
2 |
3 | export {
4 | SimpleLinearRegression,
5 | SimpleLinearRegression as SLR,
6 | } from "ml-regression-simple-linear";
7 | export { PolynomialRegression } from "ml-regression-polynomial";
8 | export { ExponentialRegression } from "ml-regression-exponential";
9 | export { PowerRegression } from "ml-regression-power";
10 | export { default as MultivariateLinearRegression } from "ml-regression-multivariate-linear";
11 | const NLR = {
12 | PotentialRegression,
13 | };
14 | export { NLR, NLR as NonLinearRegression };
15 |
16 | export {
17 | KernelRidgeRegression,
18 | KernelRidgeRegression as KRR,
19 | } from "./regression/kernel-ridge-regression";
20 | export { PolynomialFitRegression2D as PolinomialFitting2D } from "./regression/poly-fit-regression2d";
21 |
22 | // robust regressions
23 | export { TheilSenRegression } from "ml-regression-theil-sen";
24 | export { RobustPolynomialRegression } from "ml-regression-robust-polynomial";
25 |
--------------------------------------------------------------------------------
/src/regression/kernel-ridge-regression.js:
--------------------------------------------------------------------------------
1 | import Kernel from "ml-kernel";
2 | import { Matrix, solve } from "ml-matrix";
3 | import { BaseRegression } from "ml-regression-base";
4 |
5 | const defaultOptions = {
6 | lambda: 0.1,
7 | kernelType: "gaussian",
8 | kernelOptions: {},
9 | computeCoefficient: false,
10 | };
11 |
12 | // Implements the Kernel ridge regression algorithm.
13 | // http://www.ics.uci.edu/~welling/classnotes/papers_class/Kernel-Ridge.pdf
14 | export class KernelRidgeRegression extends BaseRegression {
15 | constructor(inputs, outputs, options) {
16 | super();
17 | if (inputs === true) {
18 | // reloading model
19 | this.alpha = outputs.alpha;
20 | this.inputs = outputs.inputs;
21 | this.kernelType = outputs.kernelType;
22 | this.kernelOptions = outputs.kernelOptions;
23 | this.kernel = new Kernel(outputs.kernelType, outputs.kernelOptions);
24 | } else {
25 | inputs = Matrix.checkMatrix(inputs);
26 | options = { ...defaultOptions, ...options };
27 |
28 | const kernelFunction = new Kernel(
29 | options.kernelType,
30 | options.kernelOptions,
31 | );
32 | const K = kernelFunction.compute(inputs);
33 | const n = inputs.rows;
34 | K.add(Matrix.eye(n, n).mul(options.lambda));
35 |
36 | this.alpha = solve(K, outputs);
37 | this.inputs = inputs;
38 | this.kernelType = options.kernelType;
39 | this.kernelOptions = options.kernelOptions;
40 | this.kernel = kernelFunction;
41 | }
42 | }
43 |
44 | _predict(newInputs) {
45 | return this.kernel
46 | .compute([newInputs], this.inputs)
47 | .mmul(this.alpha)
48 | .getRow(0);
49 | }
50 |
51 | toJSON() {
52 | return {
53 | name: "kernelRidgeRegression",
54 | alpha: this.alpha,
55 | inputs: this.inputs,
56 | kernelType: this.kernelType,
57 | kernelOptions: this.kernelOptions,
58 | };
59 | }
60 |
61 | static load(json) {
62 | if (json.name !== "kernelRidgeRegression") {
63 | throw new TypeError("not a KRR model");
64 | }
65 | return new KernelRidgeRegression(true, json);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/regression/poly-fit-regression2d.js:
--------------------------------------------------------------------------------
1 | import { Matrix, SVD } from "ml-matrix";
2 | import { BaseRegression } from "ml-regression-base";
3 |
4 | const defaultOptions = {
5 | order: 2,
6 | };
7 | // Implements the Kernel ridge regression algorithm.
8 | // http://www.ics.uci.edu/~welling/classnotes/papers_class/Kernel-Ridge.pdf
9 | export class PolynomialFitRegression2D extends BaseRegression {
10 | /**
11 | * Constructor for the 2D polynomial fitting
12 | * @param inputs
13 | * @param outputs
14 | * @param options
15 | */
16 | constructor(inputs, outputs, options = {}) {
17 | super();
18 | if (inputs === true) {
19 | // reloading model
20 | this.coefficients = Matrix.columnVector(outputs.coefficients);
21 | this.order = outputs.order;
22 | if (outputs.r) {
23 | this.r = outputs.r;
24 | this.r2 = outputs.r2;
25 | }
26 | if (outputs.chi2) {
27 | this.chi2 = outputs.chi2;
28 | }
29 | } else {
30 | options = { ...defaultOptions, ...options };
31 | this.order = options.order;
32 | this.coefficients = [];
33 | this.X = inputs;
34 | this.y = outputs;
35 |
36 | this.train(this.X, this.y, options);
37 | }
38 | }
39 |
40 | /**
41 | * Function that fits the model given the data(X) and predictions(y).
42 | * The third argument is an object with the following options:
43 | * order: order of the polynomial to fit.
44 | * @param {Matrix} X - A matrix with n rows and 2 columns.
45 | * @param {Matrix} y - A vector of the prediction values.
46 | */
47 | train(X, y) {
48 | if (!Matrix.isMatrix(X)) X = new Matrix(X);
49 | if (!Matrix.isMatrix(y)) y = Matrix.columnVector(y);
50 |
51 | if (y.rows !== X.rows) {
52 | y = y.transpose();
53 | }
54 |
55 | if (X.columns !== 2) {
56 | throw new RangeError(
57 | `You give X with ${X.columns} columns and it must be 2`,
58 | );
59 | }
60 | if (X.rows !== y.rows) {
61 | throw new RangeError("X and y must have the same rows");
62 | }
63 |
64 | let examples = X.rows;
65 | let coefficients = ((this.order + 2) * (this.order + 1)) / 2;
66 | if (examples < coefficients) {
67 | throw new Error(
68 | "Insufficient number of points to create regression model.",
69 | );
70 | }
71 | this.coefficients = new Array(coefficients);
72 |
73 | let x1 = X.getColumnVector(0);
74 | let x2 = X.getColumnVector(1);
75 |
76 | let scaleX1 = 1 / x1.clone().abs().max();
77 | let scaleX2 = 1 / x2.clone().abs().max();
78 | let scaleY = 1 / y.clone().abs().max();
79 |
80 | x1.mulColumn(0, scaleX1);
81 | x2.mulColumn(0, scaleX2);
82 | y.mulColumn(0, scaleY);
83 |
84 | let A = new Matrix(examples, coefficients);
85 | let col = 0;
86 |
87 | for (let i = 0; i <= this.order; ++i) {
88 | let limit = this.order - i;
89 | for (let j = 0; j <= limit; ++j) {
90 | let result = powColVector(x1, i).mulColumnVector(powColVector(x2, j));
91 | A.setColumn(col, result);
92 | col++;
93 | }
94 | }
95 |
96 | let svd = new SVD(A.transpose(), {
97 | computeLeftSingularVectors: true,
98 | computeRightSingularVectors: true,
99 | autoTranspose: false,
100 | });
101 |
102 | let qqs = Matrix.rowVector(svd.diagonal);
103 | qqs = qqs.apply((i, j) => {
104 | if (qqs.get(i, j) >= 1e-15) qqs.set(i, j, 1 / qqs.get(i, j));
105 | else qqs.set(i, j, 0);
106 | });
107 |
108 | let qqs1 = Matrix.zeros(examples, coefficients);
109 | for (let i = 0; i < coefficients; ++i) {
110 | qqs1.set(i, i, qqs.get(0, i));
111 | }
112 |
113 | qqs = qqs1;
114 |
115 | let U = svd.rightSingularVectors;
116 | let V = svd.leftSingularVectors;
117 |
118 | this.coefficients = V.mmul(qqs.transpose()).mmul(U.transpose()).mmul(y);
119 |
120 | col = 0;
121 |
122 | for (let i = 0; i <= coefficients; ++i) {
123 | let limit = this.order - i;
124 | for (let j = 0; j <= limit; ++j) {
125 | this.coefficients.set(
126 | col,
127 | 0,
128 | (this.coefficients.get(col, 0) * scaleX1 ** i * scaleX2 ** j) /
129 | scaleY,
130 | );
131 | col++;
132 | }
133 | }
134 | }
135 |
136 | _predict(newInputs) {
137 | let x1 = newInputs[0];
138 | let x2 = newInputs[1];
139 |
140 | let y = 0;
141 | let column = 0;
142 |
143 | for (let i = 0; i <= this.order; i++) {
144 | for (let j = 0; j <= this.order - i; j++) {
145 | y += x1 ** i * x2 ** j * this.coefficients.get(column, 0);
146 | column++;
147 | }
148 | }
149 |
150 | return y;
151 | }
152 |
153 | toJSON() {
154 | return {
155 | name: "polyfit2D",
156 | order: this.order,
157 | coefficients: this.coefficients,
158 | };
159 | }
160 |
161 | static load(json) {
162 | if (json.name !== "polyfit2D") {
163 | throw new TypeError("not a polyfit2D model");
164 | }
165 | return new PolynomialFitRegression2D(true, json);
166 | }
167 | }
168 |
169 | /**
170 | * Function that given a column vector return this: vector^power
171 | * @param x - Column vector.
172 | * @param power - Pow number.
173 | * @returns {Suite|Matrix}
174 | */
175 | function powColVector(x, power) {
176 | let result = x.clone();
177 | for (let i = 0; i < x.rows; ++i) {
178 | result.set(i, 0, result.get(i, 0) ** power);
179 | }
180 | return result;
181 | }
182 |
--------------------------------------------------------------------------------
/src/regression/potential-regression.js:
--------------------------------------------------------------------------------
1 | import { BaseRegression, maybeToPrecision } from "ml-regression-base";
2 | import { PolynomialRegression } from "ml-regression-polynomial";
3 |
4 | /*
5 | * Function that calculate the potential fit in the form f(x) = A*x^M
6 | * with a given M and return de A coefficient.
7 | *
8 | * @param {Vector} X - Vector of the x positions of the points.
9 | * @param {Vector} Y - Vector of the x positions of the points.
10 | * @param {Number} M - The exponent of the potential fit.
11 | * @return {Number} A - The A coefficient of the potential fit.
12 | */
13 | export class PotentialRegression extends BaseRegression {
14 | /**
15 | * @class
16 | * @param x - Independent variable
17 | * @param y - Dependent variable
18 | * @param M
19 | */
20 | constructor(x, y, M) {
21 | super();
22 | if (x === true) {
23 | // reloading model
24 | this.A = y.A;
25 | this.M = y.M;
26 | } else {
27 | let n = x.length;
28 | if (n !== y.length) {
29 | throw new RangeError("input and output array have a different length");
30 | }
31 |
32 | let linear = new PolynomialRegression(x, y, [M]);
33 | this.A = linear.coefficients[0];
34 | this.M = M;
35 | }
36 | }
37 |
38 | _predict(x) {
39 | return this.A * x ** this.M;
40 | }
41 |
42 | toJSON() {
43 | return {
44 | name: "potentialRegression",
45 | A: this.A,
46 | M: this.M,
47 | };
48 | }
49 |
50 | toString(precision) {
51 | return `f(x) = ${maybeToPrecision(this.A, precision)} * x^${this.M}`;
52 | }
53 |
54 | toLaTeX(precision) {
55 | if (this.M >= 0) {
56 | return `f(x) = ${maybeToPrecision(this.A, precision)}x^{${this.M}}`;
57 | } else {
58 | return `f(x) = \\frac{${maybeToPrecision(this.A, precision)}}{x^{${-this
59 | .M}}}`;
60 | }
61 | }
62 |
63 | static load(json) {
64 | if (json.name !== "potentialRegression") {
65 | throw new TypeError("not a potential regression model");
66 | }
67 | return new PotentialRegression(true, json);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------