ootk
Version:
Orbital Object Toolkit including Multiple Propagators, Initial Orbit Determination, and Maneuver Calculations.
110 lines (97 loc) • 3.46 kB
text/typescript
/**
* @author @thkruz Theodore Kruczek
* @license AGPL-3.0-or-later
* @copyright (c) 2025 Kruczek Labs LLC
*
* Orbital Object ToolKit is free software: you can redistribute it and/or modify it under the
* terms of the GNU Affero General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* Orbital Object ToolKit is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with
* Orbital Object ToolKit. If not, see <http://www.gnu.org/licenses/>.
*/
/* eslint-disable require-jsdoc */
/* eslint-disable func-style */
import { evalPoly } from '../main.js';
import { DownhillSimplex } from './DownhillSimplex.js';
import { PolynomicalRegressionResult } from './PolynomicalRegressionResult.js';
// / Polynomial regression optimizer.
export class PolynomialRegression {
private constructor() {
// disable constructor
}
private static _bayesInformationCriterea(n: number, k: number, sse: number): number {
return n * Math.log(sse) + k * Math.log(n);
}
/**
* Optimize polynomial coefficients to fit data series [xs] and [ys] for the
* provided polynomial [order].
* @param xs x values
* @param ys y values
* @param order Polynomial order
* @param root0 Root0
* @param root0.printIter Root0.printIter
* @returns The optimal input value.
*/
static solve(
xs: Float64Array,
ys: Float64Array,
order: number,
{ printIter = false }: { printIter?: boolean } = {},
): PolynomicalRegressionResult {
const simplex = DownhillSimplex.generateSimplex(Float64Array.from(Array(order + 1).fill(1.0)));
/**
* Sum of squared errors.
* @param coeffs Polynomial coefficients
* @returns Sum of squared errors
*/
function f(coeffs: Float64Array): number {
let sse = 0.0;
for (let i = 0; i < xs.length; i++) {
const diff = ys[i] - evalPoly(xs[i], coeffs);
sse += diff * diff;
}
return sse;
}
const result = DownhillSimplex.solveSimplex(f, simplex, {
adaptive: true,
printIter,
});
const sse = f(result);
return new PolynomicalRegressionResult(
result,
Math.sqrt(sse),
PolynomialRegression._bayesInformationCriterea(xs.length, order, sse),
);
}
/**
* Optimize polynomial coefficients to fit data series [xs] and [ys], and
* attempt to find an optimal order within the [minOrder] and
* [maxOrder] bounds.
* @param xs x values
* @param ys y values
* @param minOrder Minimum polynomial order
* @param maxOrder Maximum polynomial order
* @param root0 Root0
* @param root0.printIter Root0.printIter
* @returns The optimal input value.
*/
static solveOrder(
xs: Float64Array,
ys: Float64Array,
minOrder: number,
maxOrder: number,
{ printIter = false }: { printIter?: boolean } = {},
): PolynomicalRegressionResult {
const cache: PolynomicalRegressionResult[] = [];
for (let order = minOrder; order <= maxOrder; order++) {
cache.push(PolynomialRegression.solve(xs, ys, order, { printIter }));
}
cache.sort((a, b) => a.bic - b.bic);
return cache[0];
}
}