├── .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 | --------------------------------------------------------------------------------