{"size": 1711, "ext": "js", "max_stars_repo_path": "lib/model/rvm.js", "max_stars_repo_name": "ai-on-browser/ai-on-browser.github.io", "max_stars_repo_stars_event_min_datetime": "2021-05-12T06:28:29.000Z", "max_stars_repo_stars_event_max_datetime": "2022-01-09T11:11:40.000Z", "max_issues_repo_path": "lib/model/rvm.js", "max_issues_repo_name": "ai-on-browser/ai-on-browser.github.io", "max_issues_repo_issues_event_min_datetime": "2022-01-29T03:19:07.000Z", "max_issues_repo_issues_event_max_datetime": "2022-03-29T14:33:39.000Z", "max_forks_repo_path": "lib/model/rvm.js", "max_forks_repo_name": "ai-on-browser/ai-on-browser.github.io", "max_forks_repo_forks_event_min_datetime": null, "max_forks_repo_forks_event_max_datetime": null}
{"max_stars_count": 7, "max_issues_count": 57, "max_forks_count": null, "avg_line_length": 20.8658536585, "max_line_length": 62, "alphanum_fraction": 0.5505552309}
import { Matrix } from '../util/math.js'

/**
 * Relevance vector machine
 */
export default class RVM {
	// https://qiita.com/ctgk/items/ee512530618a5eeccd1a
	// https://en.wikipedia.org/wiki/Relevance_vector_machine
	constructor() {
		this._alpha = 1.0
		this._beta = 1.0
	}

	_kernel(x, y) {
		const k = x.copySub(y)
		return Math.exp(-10 * k.norm() ** 2)
	}

	/**
	 * Fit model.
	 * @param {Array<Array<number>>} x
	 * @param {Array<Array<number>>} y
	 */
	fit(x, y) {
		this._x = x = Matrix.fromArray(x)
		y = Matrix.fromArray(y)
		const n = x.rows

		const p = new Matrix(n, n)
		for (let i = 0; i < n; i++) {
			for (let j = i; j < n; j++) {
				const k = this._kernel(x.row(i), x.row(j))
				p.set(i, j, k)
				p.set(j, i, k)
			}
		}
		const a = Array(n).fill(this._alpha)

		let maxCount = 1
		while (maxCount-- > 0) {
			const prec = p.tDot(p)
			prec.mult(this._beta)
			prec.add(Matrix.diag(a))
			this._cov = prec.inv()

			this._mean = this._cov.dot(p.tDot(y))
			this._mean.mult(this._beta)

			const g = []
			for (let i = 0; i < n; i++) {
				g.push(1 - a[i] * this._cov.at(i, i))
				a[i] = g[i] / Math.sqrt(this._mean.at(i, 0))
			}
			const tmp = y.copySub(p.dot(this._mean))
			tmp.map(v => v ** 2)

			this._beta = (n - g.reduce((s, v) => s + v, 0)) / tmp.sum()
		}
	}

	/**
	 * Returns predicted values.
	 * @param {Array<Array<number>>} x
	 * @returns {number[]}
	 */
	predict(x) {
		const n = this._x.rows
		x = Matrix.fromArray(x)
		const m = x.rows
		const k = new Matrix(m, n)
		for (let i = 0; i < m; i++) {
			for (let j = 0; j < n; j++) {
				const v = this._kernel(x.row(i), this._x.row(j))
				k.set(i, j, v)
			}
		}

		const mean = k.dot(this._mean)
		return mean.value
	}
}
