Skip to content

Commit 4c07226

Browse files
committed
initial commit
0 parents  commit 4c07226

11 files changed

+468
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.DS_Store
2+
node_modules

README.md

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
## Rootfinder
2+
3+
Rootfinder is a TypeScript implementation of Cem Yuksel's fast, robust, and simple root finding algorithm presented in the paper "High-Performance Polynomial Root Finding for Graphics". It can be used to find real roots of polynomials of degree 10 and higher.
4+
5+
### Usage
6+
7+
```typescript
8+
const f = new Polynomial([-7412, -1505, -20, -10, 1]);
9+
10+
const startSearchInterval = -100;
11+
const endSearchInterval = 100;
12+
const epsilon = 1e-6;
13+
14+
const roots = polynomialRoots(f, startSearchInterval, endSearchInterval, epsilon);
15+
```
16+
17+
### Installing
18+
19+
> npm install rootfinder
20+
21+
or using yarn
22+
23+
> yarn add rootfinder
24+
25+
### Paper
26+
27+
Cem Yuksel. 2022. High-Performance Polynomial Root Finding for Graphics. Proc. ACM Comput. Graph. Interact. Tech. 5, 3, Article 7 (July 2022), 15 pages.

dist/index.d.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Class representing a polynomial.
3+
*/
4+
export declare class Polynomial {
5+
readonly coefficients: readonly number[];
6+
readonly degree: number;
7+
/**
8+
* @param coefficients coefficients in order of increasing power eg 1 + 2x + 3x^2 - 12x^3 => [1, 2, 3, -12]
9+
*/
10+
constructor(coefficients: readonly number[]);
11+
evaluate(x: number): number;
12+
derivative(): Polynomial;
13+
}
14+
/**
15+
* Find the roots of polynomial using the method described in
16+
* High-Performance Polynomial Root Finding for Graphics (Yuksel 2022)
17+
*
18+
* @param f polynomial to find the roots of
19+
* @param startInterval beginning of interval to search
20+
* @param endInterval end of interval to search
21+
* @param epsilon tolerance for root finding
22+
* @returns An array of roots
23+
*/
24+
export declare function polynomialRoots(f: Polynomial, startInterval: number, endInterval: number, epsilon: number): number[];

dist/index.js

+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
"use strict";
2+
Object.defineProperty(exports, "__esModule", { value: true });
3+
exports.polynomialRoots = exports.Polynomial = void 0;
4+
/**
5+
* Class representing a polynomial.
6+
*/
7+
class Polynomial {
8+
/**
9+
* @param coefficients coefficients in order of increasing power eg 1 + 2x + 3x^2 - 12x^3 => [1, 2, 3, -12]
10+
*/
11+
constructor(coefficients) {
12+
this.coefficients = coefficients;
13+
this.degree = coefficients.length - 1;
14+
}
15+
evaluate(x) {
16+
let result = 0;
17+
for (let i = 0, l = this.coefficients.length; i < l; i++) {
18+
result += this.coefficients[i] * Math.pow(x, i);
19+
}
20+
return result;
21+
}
22+
derivative() {
23+
if (this.degree === 0) {
24+
throw new Error("Cannot take derivative of constant polynomial");
25+
}
26+
const coefficients = [];
27+
for (let i = 1; i < this.coefficients.length; i++) {
28+
coefficients.push(this.coefficients[i] * i);
29+
}
30+
return new Polynomial(coefficients);
31+
}
32+
}
33+
exports.Polynomial = Polynomial;
34+
/**
35+
* Find the roots of polynomial using the method described in
36+
* High-Performance Polynomial Root Finding for Graphics (Yuksel 2022)
37+
*
38+
* @param f polynomial to find the roots of
39+
* @param startInterval beginning of interval to search
40+
* @param endInterval end of interval to search
41+
* @param epsilon tolerance for root finding
42+
* @returns An array of roots
43+
*/
44+
function polynomialRoots(f, startInterval, endInterval, epsilon) {
45+
if (f.degree === 2) {
46+
return findQuadraticRoots(f, startInterval, endInterval);
47+
}
48+
const derivative = f.derivative();
49+
const rootsOfDerivative = polynomialRoots(derivative, startInterval, endInterval, epsilon);
50+
rootsOfDerivative.push(endInterval);
51+
let a = startInterval;
52+
const roots = [];
53+
for (let i = 0; i < rootsOfDerivative.length; i++) {
54+
let b = rootsOfDerivative[i];
55+
if (Math.sign(f.evaluate(a)) !== Math.sign(f.evaluate(b))) {
56+
roots.push(findRoot(f, derivative, a, b, epsilon));
57+
}
58+
a = b;
59+
}
60+
return roots;
61+
}
62+
exports.polynomialRoots = polynomialRoots;
63+
function findQuadraticRoots(f, startInterval, endInterval) {
64+
const c = f.coefficients[0];
65+
const b = f.coefficients[1];
66+
const a = f.coefficients[2];
67+
const delta = b * b - 4 * a * c;
68+
if (delta >= 0) {
69+
const d = Math.sqrt(delta);
70+
const q = -0.5 * (b + multSign(d, b));
71+
const rv0 = q / a;
72+
const rv1 = c / q;
73+
const res = [];
74+
const aa = Math.min(rv0, rv1);
75+
const bb = Math.max(rv0, rv1);
76+
if (aa >= startInterval && aa <= endInterval) {
77+
res.push(aa);
78+
}
79+
if (bb >= startInterval && bb <= endInterval) {
80+
res.push(bb);
81+
}
82+
return res;
83+
}
84+
return [];
85+
}
86+
function multSign(v, sign) {
87+
return v * (sign < 0 ? -1 : 1);
88+
}
89+
// Following http://www.cemyuksel.com/research/polynomials/polynomial_roots_hpg2022_supplemental.pdf
90+
function findRoot(f, deriv, x1, x2, epsilon) {
91+
let xr = (x1 + x2) / 2;
92+
if (Math.abs(x2 - x1) <= 2 * epsilon) {
93+
return xr;
94+
}
95+
if (f.degree === 3) {
96+
let xn = -0;
97+
for (let i = 0; i < 10; i++) {
98+
xn = xr - f.evaluate(xr) / deriv.evaluate(xr);
99+
xn = Math.max(x1, Math.min(x2, xn));
100+
if (Math.abs(xr - xn) <= epsilon) {
101+
return xn;
102+
}
103+
xr = xn;
104+
}
105+
if (xr < x1 || xr > x2) {
106+
xr = (x1 + x2) / 2;
107+
}
108+
}
109+
const y1 = f.evaluate(x1);
110+
let yr = f.evaluate(xr);
111+
while (true) {
112+
if (Math.sign(yr) === Math.sign(y1)) {
113+
x1 = xr;
114+
}
115+
else {
116+
x2 = xr;
117+
}
118+
let xn = xr - yr / deriv.evaluate(xr);
119+
if (x1 < xn && xn < x2) {
120+
if (Math.abs(xr - xn) > epsilon) {
121+
xr = xn;
122+
yr = f.evaluate(xr);
123+
}
124+
else {
125+
if (Math.sign(yr) === Math.sign(y1)) {
126+
xr = xn + epsilon;
127+
}
128+
else {
129+
xr = xn - epsilon;
130+
}
131+
const y = f.evaluate(xr);
132+
if (Math.sign(y) !== Math.sign(yr)) {
133+
return xn;
134+
}
135+
else {
136+
yr = y;
137+
}
138+
}
139+
}
140+
else {
141+
xr = (x1 + x2) / 2;
142+
if (xr === x1 || xr === x2 || x2 - x1 <= 2 * epsilon) {
143+
return xr;
144+
}
145+
else {
146+
yr = f.evaluate(xr);
147+
}
148+
}
149+
}
150+
}

package.json

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "rootfinder",
3+
"version": "1.0.0",
4+
"description": "A TypeScript implementation of High-Performance Polynomial Root Finding for Graphics (Yuksel 2022)",
5+
"main": "dist/index.js",
6+
"types": "dist/index.d.ts",
7+
"scripts": {
8+
"test": "ts-node test/index.ts",
9+
"build": "tsc"
10+
},
11+
"keywords": [
12+
"polynomial",
13+
"roots",
14+
"root-finding",
15+
"math"
16+
],
17+
"files": [
18+
"/dist"
19+
],
20+
"author": "Peter Boyer",
21+
"license": "MIT",
22+
"dependencies": {
23+
"@types/node": "^18.0.4",
24+
"typescript": "^4.7.4"
25+
}
26+
}

papers/polynomial_roots_hpg2022.pdf

2.95 MB
Binary file not shown.
3.26 MB
Binary file not shown.

0 commit comments

Comments
 (0)