nerdamer 2.0.0-rc.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BREAKING_CHANGES.md +244 -0
- package/README.md +168 -229
- package/dist/bundle.js +1 -1
- package/dist/bundle.js.LICENSE.txt +6 -6
- package/output/algebra/adapters.d.ts +3 -33
- package/output/algebra/adapters.js +7 -105
- package/output/algebra/algorithms/arith.js +10 -9
- package/output/algebra/algorithms/groebnerBase.d.ts +24 -20
- package/output/algebra/algorithms/groebnerBase.js +486 -723
- package/output/algebra/dispatch.d.ts +1 -0
- package/output/algebra/dispatch.js +25 -0
- package/output/algebra/factor/factor.d.ts +15 -0
- package/output/algebra/factor/factor.js +99 -12
- package/output/algebra/gcd/gcd.d.ts +8 -7
- package/output/algebra/gcd/gcd.js +32 -78
- package/output/algebra/groebner.d.ts +2 -2
- package/output/algebra/groebner.js +6 -12
- package/output/algebra/partfrac.d.ts +3 -3
- package/output/algebra/partfrac.js +28 -61
- package/output/algebra/polynomial/ModularSparsePolynomial.d.ts +131 -0
- package/output/algebra/polynomial/ModularSparsePolynomial.js +378 -0
- package/output/algebra/polynomial/ModularSparsePolynomialFactor.d.ts +40 -0
- package/output/algebra/polynomial/ModularSparsePolynomialFactor.js +661 -0
- package/output/algebra/polynomial/SparsePolynomialFactor.d.ts +64 -0
- package/output/algebra/polynomial/SparsePolynomialFactor.js +492 -0
- package/output/algebra/polynomial/SparsePolynomialGcd.d.ts +29 -0
- package/output/algebra/polynomial/SparsePolynomialGcd.js +259 -0
- package/output/algebra/polynomial/SparsePolynomialMultivariateFactor.d.ts +82 -0
- package/output/algebra/polynomial/SparsePolynomialMultivariateFactor.js +604 -0
- package/output/algebra/polynomial/modularGcd.d.ts +40 -0
- package/output/algebra/polynomial/modularGcd.js +365 -0
- package/output/algebra/polynomialize.js +4 -2
- package/output/algebra/simplify/funcsimp.js +1 -1
- package/output/algebra/simplify/ratsimp.js +1 -1
- package/output/algebra/simplify/simplify.js +11 -2
- package/output/algebra/utils.d.ts +1 -1
- package/output/algebra/utils.js +3 -3
- package/output/api/advanced.d.ts +2 -1
- package/output/api/advanced.js +3 -3
- package/output/api/algebra.d.ts +21 -4
- package/output/api/algebra.js +22 -3
- package/output/api/assumptions.d.ts +1 -1
- package/output/api/assumptions.js +2 -1
- package/output/api/calculus.d.ts +2 -1
- package/output/api/calculus.js +2 -1
- package/output/api/core.d.ts +28 -7
- package/output/api/core.js +28 -2
- package/output/api/languages/deu.d.ts +3 -0
- package/output/api/languages/deu.js +266 -0
- package/output/api/languages/fra.d.ts +3 -0
- package/output/api/languages/fra.js +266 -0
- package/output/api/languages/ita.d.ts +3 -0
- package/output/api/languages/ita.js +266 -0
- package/output/api/languages/nld.d.ts +3 -0
- package/output/api/languages/nld.js +266 -0
- package/output/api/languages/por.d.ts +3 -0
- package/output/api/languages/por.js +266 -0
- package/output/api/languages/spa.d.ts +3 -0
- package/output/api/languages/spa.js +266 -0
- package/output/api/parser.d.ts +10 -2
- package/output/api/parser.js +2 -0
- package/output/api/solve.d.ts +1 -1
- package/output/api/solve.js +2 -1
- package/output/api/structures.d.ts +1 -1
- package/output/api/structures.js +2 -1
- package/output/calculus/adapters.d.ts +8 -0
- package/output/calculus/adapters.js +41 -0
- package/output/calculus/derivative/diff.js +6 -3
- package/output/calculus/dispatch.d.ts +1 -0
- package/output/calculus/dispatch.js +25 -0
- package/output/calculus/integrate/integrate.js +4 -4
- package/output/calculus/integrate/integrationTable.js +16 -16
- package/output/calculus/laplace/ilaplace.js +1 -1
- package/output/calculus/laplace/ilaplaceTable.js +18 -17
- package/output/calculus/laplace/laplaceTable.js +3 -2
- package/output/calculus/limit/limit.js +1 -1
- package/output/core/classes/assumption/Assumption.js +14 -10
- package/output/core/classes/assumption/assume.d.ts +6 -0
- package/output/core/classes/assumption/assume.js +9 -0
- package/output/core/classes/assumption/dispatch.d.ts +1 -0
- package/output/core/classes/assumption/dispatch.js +11 -0
- package/output/core/classes/collection/Collection.js +11 -2
- package/output/core/classes/complex/Complex.d.ts +10 -0
- package/output/core/classes/complex/Complex.js +19 -1
- package/output/core/classes/decimalSet/DecimalSet.js +5 -4
- package/output/core/classes/dictionary/Dictionary.js +4 -1
- package/output/core/classes/expression/CoeffObject.d.ts +1 -1
- package/output/core/classes/expression/CoeffObject.js +1 -2
- package/output/core/classes/expression/Expression.d.ts +35 -26
- package/output/core/classes/expression/Expression.js +77 -50
- package/output/core/classes/expression/analysis.d.ts +1 -1
- package/output/core/classes/expression/analysis.js +7 -7
- package/output/core/classes/expression/format.d.ts +9 -0
- package/output/core/classes/expression/format.js +130 -7
- package/output/core/classes/expression/utils.d.ts +9 -0
- package/output/core/classes/expression/utils.js +52 -6
- package/output/core/classes/matrix/Matrix.d.ts +2 -3
- package/output/core/classes/matrix/Matrix.js +26 -23
- package/output/core/classes/matrix/dispatch.d.ts +1 -0
- package/output/core/classes/matrix/dispatch.js +19 -0
- package/output/core/classes/matrix/functions.d.ts +2 -0
- package/output/core/classes/matrix/functions.js +5 -0
- package/output/core/classes/parser/Parser.d.ts +31 -5
- package/output/core/classes/parser/Parser.js +357 -444
- package/output/core/classes/parser/constants.d.ts +2 -0
- package/output/core/classes/parser/constants.js +4 -2
- package/output/core/classes/parser/controlFlowSignals.d.ts +38 -0
- package/output/core/classes/parser/controlFlowSignals.js +60 -0
- package/output/core/classes/parser/operations/add.js +14 -16
- package/output/core/classes/parser/operations/compare.js +13 -0
- package/output/core/classes/parser/operations/functions.d.ts +3 -4
- package/output/core/classes/parser/operations/functions.js +48 -102
- package/output/core/classes/parser/operations/multiply.js +22 -15
- package/output/core/classes/parser/operations/power.js +11 -11
- package/output/core/classes/parser/operations/subtract.js +2 -1
- package/output/core/classes/parser/preprocess.d.ts +0 -7
- package/output/core/classes/parser/preprocess.js +33 -39
- package/output/core/classes/parser/scripting/controlFlow.d.ts +4 -102
- package/output/core/classes/parser/scripting/controlFlow.js +134 -186
- package/output/core/classes/parser/scripting/deferred.d.ts +4 -0
- package/output/core/classes/parser/scripting/deferred.js +65 -0
- package/output/core/classes/parser/scripting/dispatch.d.ts +1 -0
- package/output/core/classes/parser/scripting/dispatch.js +62 -0
- package/output/core/classes/parser/scripting/evaluate.d.ts +13 -0
- package/output/core/classes/parser/scripting/evaluate.js +17 -1
- package/output/core/classes/parser/scripting/scope.d.ts +1 -28
- package/output/core/classes/parser/scripting/scope.js +36 -40
- package/output/core/classes/parser/types.d.ts +5 -5
- package/output/core/classes/parser/wrappers/IndexedReference.d.ts +4 -3
- package/output/core/classes/parser/wrappers/IndexedReference.js +8 -0
- package/output/core/classes/parser/wrappers/KeyValuePair.d.ts +2 -2
- package/output/core/classes/polynomial/Polynomial.d.ts +5 -1
- package/output/core/classes/polynomial/Polynomial.js +53 -7
- package/output/core/classes/polynomial/SparsePolynomial.d.ts +221 -0
- package/output/core/classes/polynomial/SparsePolynomial.js +824 -0
- package/output/core/classes/polynomial/SparsePolynomialAdapter.d.ts +56 -0
- package/output/core/classes/polynomial/SparsePolynomialAdapter.js +241 -0
- package/output/core/classes/polynomial/Term.js +2 -2
- package/output/core/{adapters.d.ts → classes/polynomial/adapters.d.ts} +2 -2
- package/output/core/classes/polynomial/adapters.js +33 -0
- package/output/core/classes/polynomial/dispatch.d.ts +1 -0
- package/output/core/classes/polynomial/dispatch.js +14 -0
- package/output/core/classes/polynomial/functions.d.ts +8 -0
- package/output/core/classes/polynomial/functions.js +71 -8
- package/output/core/classes/polynomial/utils.js +2 -1
- package/output/core/classes/rational/Rational.d.ts +8 -11
- package/output/core/classes/rational/Rational.js +70 -20
- package/output/core/classes/seq/SEQ.js +4 -4
- package/output/core/classes/valuesSet/ValuesSet.d.ts +9 -1
- package/output/core/classes/valuesSet/ValuesSet.js +22 -4
- package/output/core/classes/vector/Vector.d.ts +2 -3
- package/output/core/classes/vector/Vector.js +11 -15
- package/output/core/classes/vector/dispatch.d.ts +1 -0
- package/output/core/classes/vector/dispatch.js +11 -0
- package/output/core/common/classes/MathematicalAggregate.js +11 -2
- package/output/core/common/classes/StructuredEntity.d.ts +0 -17
- package/output/core/common/classes/StructuredEntity.js +3 -64
- package/output/core/common/common.d.ts +0 -1
- package/output/core/common/common.js +29 -234
- package/output/core/converters/BaseConverter.js +3 -2
- package/output/core/converters/Converter.js +9 -5
- package/output/core/converters/Pattern.js +1 -1
- package/output/core/dispatch.d.ts +18 -0
- package/output/core/dispatch.js +3 -281
- package/output/core/errors.d.ts +259 -474
- package/output/core/errors.js +270 -542
- package/output/core/fullFunctions.d.ts +7 -0
- package/output/core/fullFunctions.js +31 -0
- package/output/core/functions/bigint/bigint.d.ts +13 -1
- package/output/core/functions/bigint/bigint.js +56 -4
- package/output/core/functions/bigint/primeFactor.d.ts +2 -0
- package/output/core/functions/bigint/primeFactor.js +13 -13
- package/output/core/functions/build/definitions.js +6 -0
- package/output/core/functions/complex.d.ts +2 -2
- package/output/core/functions/complex.dispatch.d.ts +1 -0
- package/output/core/functions/complex.dispatch.js +16 -0
- package/output/core/functions/complex.js +7 -7
- package/output/core/functions/decimal.js +2 -1
- package/output/core/functions/expand/expand.js +2 -2
- package/output/core/functions/numeric.d.ts +1 -1
- package/output/core/functions/string.js +2 -1
- package/output/core/functions/subst.js +21 -13
- package/output/core/parserFunctions.d.ts +7 -0
- package/output/core/parserFunctions.js +25 -0
- package/output/index.d.ts +50 -30
- package/output/index.js +147 -19
- package/output/math/defint/defint.d.ts +18 -0
- package/output/math/defint/defint.js +60 -0
- package/output/math/defint/defintDecimal.js +6 -5
- package/output/math/defint/defintNative.js +7 -18
- package/output/math/dispatch.d.ts +1 -0
- package/output/math/dispatch.js +101 -0
- package/output/math/geometry.d.ts +4 -0
- package/output/math/geometry.js +23 -0
- package/output/math/math.d.ts +59 -19
- package/output/math/math.js +244 -100
- package/output/math/trig.js +4 -4
- package/output/math/utils.d.ts +6 -0
- package/output/math/utils.js +39 -0
- package/output/solve/classes/PolynomialSolver.d.ts +33 -14
- package/output/solve/classes/PolynomialSolver.js +267 -67
- package/output/solve/classes/SolutionSet.d.ts +20 -1
- package/output/solve/classes/SolutionSet.js +82 -5
- package/output/solve/dispatch.d.ts +1 -0
- package/output/solve/dispatch.js +13 -0
- package/output/solve/linsolve.d.ts +2 -0
- package/output/solve/linsolve.js +13 -5
- package/output/solve/solve.d.ts +10 -0
- package/output/solve/solve.js +128 -20
- package/output/solve/solveSystem.d.ts +1 -1
- package/output/solve/solveSystem.js +8 -32
- package/output/solve/utils.d.ts +2 -1
- package/output/solve/utils.js +18 -22
- package/output/utils/array.d.ts +1 -1
- package/output/utils/debug.js +2 -1
- package/package.json +22 -38
- package/dist/parser.js +0 -2
- package/dist/parser.js.LICENSE.txt +0 -7
- package/docs-data/parser-functions.json +0 -1960
- package/output/algebra/algorithms/factor.d.ts +0 -3
- package/output/algebra/algorithms/factor.js +0 -5
- package/output/algebra/algorithms/factorMultivariate.d.ts +0 -23
- package/output/algebra/algorithms/factorMultivariate.js +0 -2393
- package/output/algebra/algorithms/factorUnivariate.d.ts +0 -72
- package/output/algebra/algorithms/factorUnivariate.js +0 -1072
- package/output/algebra/algorithms/gcd.d.ts +0 -22
- package/output/algebra/algorithms/gcd.js +0 -690
- package/output/algebra/algorithms/multiPoly/MultiPoly.d.ts +0 -137
- package/output/algebra/algorithms/multiPoly/MultiPoly.js +0 -346
- package/output/algebra/algorithms/poly.d.ts +0 -228
- package/output/algebra/algorithms/poly.js +0 -1299
- package/output/algebra/algorithms/rational.d.ts +0 -17
- package/output/algebra/algorithms/rational.js +0 -115
- package/output/core/adapters.js +0 -40
- package/output/core/classes/parser/operations/comma.d.ts +0 -7
- package/output/core/classes/parser/operations/comma.js +0 -11
package/output/math/trig.js
CHANGED
|
@@ -402,8 +402,8 @@ function tan(x) {
|
|
|
402
402
|
retval = Expression_1.Expression.Number(x.getMultiplier().toDecimal().tan());
|
|
403
403
|
}
|
|
404
404
|
else if (Settings_1.Settings.EVALUATE && x.isComplex()) {
|
|
405
|
-
const dblRe = x.realPart().times(
|
|
406
|
-
const dblIm = x.imagPart().times(
|
|
405
|
+
const dblRe = x.realPart().times((0, shortcuts_1.two)());
|
|
406
|
+
const dblIm = x.imagPart().times((0, shortcuts_1.two)());
|
|
407
407
|
const d = cos(dblRe).plus(cosh(dblIm));
|
|
408
408
|
retval = sin(dblRe).div(d).plus(sinh(dblIm).div(d).i());
|
|
409
409
|
}
|
|
@@ -599,7 +599,7 @@ function acos(x) {
|
|
|
599
599
|
let retval;
|
|
600
600
|
if (Settings_1.Settings.EVALUATE && x.isNUM()) {
|
|
601
601
|
// Becomes complex between [-1, 1]
|
|
602
|
-
if (x.lt(
|
|
602
|
+
if (x.lt((0, shortcuts_1.minusOne)()) || x.gt((0, shortcuts_1.one)())) {
|
|
603
603
|
const sgn = x.sign();
|
|
604
604
|
const halfPi = Expression_1.Expression.Pi().div((0, shortcuts_1.two)());
|
|
605
605
|
let asinX = asin(x.abs());
|
|
@@ -1225,7 +1225,7 @@ function acosh(x) {
|
|
|
1225
1225
|
retval = Expression_1.Expression.Pi().div((0, shortcuts_1.two)()).times(Expression_1.Expression.Img());
|
|
1226
1226
|
}
|
|
1227
1227
|
else if (Settings_1.Settings.EVALUATE) {
|
|
1228
|
-
if (x.isComplex() || x.lt(
|
|
1228
|
+
if (x.isComplex() || x.lt((0, shortcuts_1.one)())) {
|
|
1229
1229
|
const a = (0, math_1.sqrt)(x.plus((0, shortcuts_1.one)()));
|
|
1230
1230
|
const b = (0, math_1.sqrt)(x.minus((0, shortcuts_1.one)()));
|
|
1231
1231
|
const c = x.plus(a.times(b));
|
package/output/math/utils.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
import { Collection } from '../core/classes/collection/Collection';
|
|
1
2
|
import { Expression } from '../core/classes/expression/Expression';
|
|
2
3
|
export { product, stripPower, sum } from '../core/classes/expression/utils';
|
|
3
4
|
import type { ExpressionInput, ParserEntity } from '../core/types';
|
|
5
|
+
/**
|
|
6
|
+
* Returns the continued-fraction decomposition as a Collection containing
|
|
7
|
+
* sign, whole part, and a Vector of partial quotients.
|
|
8
|
+
*/
|
|
9
|
+
export declare function continuedFraction(x: ExpressionInput, depth?: ExpressionInput): Collection;
|
|
4
10
|
/**
|
|
5
11
|
* Contains useful functions which are not likely to be exported to the user through `build`
|
|
6
12
|
*/
|
package/output/math/utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.sum = exports.stripPower = exports.product = void 0;
|
|
4
|
+
exports.continuedFraction = continuedFraction;
|
|
4
5
|
exports.toFactor = toFactor;
|
|
5
6
|
exports.roots = roots;
|
|
6
7
|
exports.getCombinations = getCombinations;
|
|
@@ -23,7 +24,45 @@ const Matrix_1 = require("../core/classes/matrix/Matrix");
|
|
|
23
24
|
const constants_1 = require("../core/classes/parser/constants");
|
|
24
25
|
const ValuesSet_1 = require("../core/classes/valuesSet/ValuesSet");
|
|
25
26
|
const Vector_1 = require("../core/classes/vector/Vector");
|
|
27
|
+
const errors_1 = require("../core/errors");
|
|
26
28
|
const primeFactor_1 = require("../core/functions/bigint/primeFactor");
|
|
29
|
+
/**
|
|
30
|
+
* Returns the continued-fraction decomposition as a Collection containing
|
|
31
|
+
* sign, whole part, and a Vector of partial quotients.
|
|
32
|
+
*/
|
|
33
|
+
function continuedFraction(x, depth = 10) {
|
|
34
|
+
const input = Expression_1.Expression.create(x);
|
|
35
|
+
const limitExpression = Expression_1.Expression.create(depth);
|
|
36
|
+
if (!limitExpression.isNUM() ||
|
|
37
|
+
!limitExpression.isInteger() ||
|
|
38
|
+
limitExpression.lt((0, shortcuts_1.zero)()) ||
|
|
39
|
+
limitExpression.getMultiplier().numerator > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
40
|
+
throw new errors_1.UnexpectedInputError((0, errors_1.message)('wrongInput', {
|
|
41
|
+
expected: 'a non-negative integer',
|
|
42
|
+
received: limitExpression.text(),
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
const limit = Number(limitExpression.getMultiplier().numerator);
|
|
46
|
+
const evaluated = input.isConstant() ? input.evaluate() : input;
|
|
47
|
+
if (!evaluated.isNUM()) {
|
|
48
|
+
return new Collection_1.Collection([(0, shortcuts_1.one)(), input, new Vector_1.Vector()]);
|
|
49
|
+
}
|
|
50
|
+
const rational = evaluated.getMultiplier();
|
|
51
|
+
const sign = Expression_1.Expression.Number(rational.sign());
|
|
52
|
+
const numerator = rational.numerator < 0n ? -rational.numerator : rational.numerator;
|
|
53
|
+
let denominator = rational.denominator;
|
|
54
|
+
const whole = numerator / denominator;
|
|
55
|
+
let remainder = numerator % denominator;
|
|
56
|
+
const fractions = [];
|
|
57
|
+
for (let i = 0; i < limit && remainder !== 0n; i++) {
|
|
58
|
+
const quotient = denominator / remainder;
|
|
59
|
+
fractions.push(Expression_1.Expression.Number(quotient));
|
|
60
|
+
const nextRemainder = denominator % remainder;
|
|
61
|
+
denominator = remainder;
|
|
62
|
+
remainder = nextRemainder;
|
|
63
|
+
}
|
|
64
|
+
return new Collection_1.Collection([sign, Expression_1.Expression.Number(whole), new Vector_1.Vector(fractions)]);
|
|
65
|
+
}
|
|
27
66
|
/**
|
|
28
67
|
* Contains useful functions which are not likely to be exported to the user through `build`
|
|
29
68
|
*/
|
|
@@ -6,8 +6,8 @@ import { Polynomial } from '../../core/classes/polynomial/Polynomial';
|
|
|
6
6
|
* Finds all complex roots of a univariate polynomial numerically.
|
|
7
7
|
*
|
|
8
8
|
* Coefficient arrays use ascending powers: the element at index `k` is the
|
|
9
|
-
* coefficient of `x^k`. Polynomial and string inputs are converted
|
|
10
|
-
*
|
|
9
|
+
* coefficient of `x^k`. Expression, Polynomial, and string inputs are converted directly
|
|
10
|
+
* to numerical complex coefficients before solving. Linear and quadratic inputs are handled
|
|
11
11
|
* directly; higher degrees use simultaneous Aberth iteration followed by
|
|
12
12
|
* Newton refinement.
|
|
13
13
|
*
|
|
@@ -36,37 +36,50 @@ export declare class PolynomialSolver {
|
|
|
36
36
|
/**
|
|
37
37
|
* Creates a polynomial root solver.
|
|
38
38
|
*
|
|
39
|
-
* @param input - A polynomial,
|
|
40
|
-
* coefficients.
|
|
39
|
+
* @param input - A polynomial expression, Polynomial, polynomial string, or ascending-power
|
|
40
|
+
* Decimal coefficients.
|
|
41
41
|
* @param variable - Variable expected in polynomial or string input. It is used to reject
|
|
42
42
|
* a conflicting variable or multivariate polynomial.
|
|
43
43
|
* @param precision - Working `decimal.js` precision used while finding roots.
|
|
44
44
|
* @param epsilon - Numerical convergence tolerance. Defaults to `1e-14`.
|
|
45
|
-
* @throws {@link core!UnexpectedInputError} If
|
|
46
|
-
*
|
|
45
|
+
* @throws {@link core!UnexpectedInputError} If symbolic input is multivariate, uses a
|
|
46
|
+
* variable different from `variable`, or has nonnumeric coefficients.
|
|
47
47
|
*/
|
|
48
|
-
constructor(input: Decimal[] | Polynomial | string, variable?: string, precision?: number, epsilon?: Decimal);
|
|
48
|
+
constructor(input: Decimal[] | Polynomial | Expression | string, variable?: string, precision?: number, epsilon?: Decimal);
|
|
49
49
|
/**
|
|
50
50
|
* Aberth method: simultaneous approximation of all roots
|
|
51
51
|
* More stable than deflation for high-degree polynomials
|
|
52
52
|
*/
|
|
53
53
|
private aberthMethod;
|
|
54
|
+
private complexSqrt;
|
|
54
55
|
/**
|
|
55
|
-
*
|
|
56
|
+
* Evaluates a polynomial and its derivative together using Horner's method.
|
|
56
57
|
*/
|
|
57
|
-
private
|
|
58
|
-
private complexSqrt;
|
|
58
|
+
private evaluateWithDerivative;
|
|
59
59
|
private finalize;
|
|
60
|
+
private isNumericalRoot;
|
|
61
|
+
/**
|
|
62
|
+
* Uses native double-precision arithmetic to locate starting points for Decimal refinement.
|
|
63
|
+
*
|
|
64
|
+
* Returning `null` leaves the existing Decimal Aberth implementation responsible for the
|
|
65
|
+
* complete solve.
|
|
66
|
+
*/
|
|
67
|
+
private nativeAberthSeeds;
|
|
60
68
|
/**
|
|
61
69
|
* Refine a single root using Newton's method
|
|
62
70
|
*/
|
|
63
71
|
private newtonRefine;
|
|
64
|
-
private solveQuadratic;
|
|
65
72
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
73
|
+
* Computes a Lagrange-Fujiwara upper bound for root magnitudes.
|
|
74
|
+
*
|
|
75
|
+
* For a polynomial with leading coefficient a_n, every root lies within
|
|
76
|
+
* 2 * max(|a_(n-i) / a_n|^(1/i)). This is substantially tighter than the
|
|
77
|
+
* simple Cauchy bound for polynomials with large middle coefficients.
|
|
78
|
+
*
|
|
79
|
+
* @see https://doi.org/10.1016/S0377-0427(03)00381-9
|
|
68
80
|
*/
|
|
69
|
-
private
|
|
81
|
+
private rootMagnitudeBound;
|
|
82
|
+
private solveQuadratic;
|
|
70
83
|
/**
|
|
71
84
|
* Computes the roots of the configured polynomial.
|
|
72
85
|
*
|
|
@@ -82,4 +95,10 @@ export declare class PolynomialSolver {
|
|
|
82
95
|
*/
|
|
83
96
|
roots(asExpressions?: true): Expression[];
|
|
84
97
|
roots(asExpressions?: false): Complex[];
|
|
98
|
+
/**
|
|
99
|
+
* Computes roots numerically, rejects candidates with an excessive polynomial residual,
|
|
100
|
+
* and converts accepted values only after numerical validation is complete.
|
|
101
|
+
*/
|
|
102
|
+
validatedRoots(asExpressions?: true): Expression[];
|
|
103
|
+
validatedRoots(asExpressions?: false): Complex[];
|
|
85
104
|
}
|
|
@@ -9,6 +9,7 @@ const Complex_1 = require("../../core/classes/complex/Complex");
|
|
|
9
9
|
const Expression_1 = require("../../core/classes/expression/Expression");
|
|
10
10
|
const Polynomial_1 = require("../../core/classes/polynomial/Polynomial");
|
|
11
11
|
const errors_1 = require("../../core/errors");
|
|
12
|
+
const bigint_1 = require("../../core/functions/bigint/bigint");
|
|
12
13
|
const DEFAULT_PRECISION = 50;
|
|
13
14
|
const DEFAULT_EPSILON = 1e-14;
|
|
14
15
|
const ABERTH_MAX_ITERATIONS = 100;
|
|
@@ -18,12 +19,13 @@ const ABERTH_ROOT_SPACING_SCALE = 0.01;
|
|
|
18
19
|
const ABERTH_CONVERGENCE_SCALE = 1e-2;
|
|
19
20
|
const NEWTON_MAX_ITERATIONS = 20;
|
|
20
21
|
const NEWTON_CONVERGENCE_SCALE = 1e-6;
|
|
22
|
+
const NATIVE_EPSILON_SCALE = 16;
|
|
21
23
|
/**
|
|
22
24
|
* Finds all complex roots of a univariate polynomial numerically.
|
|
23
25
|
*
|
|
24
26
|
* Coefficient arrays use ascending powers: the element at index `k` is the
|
|
25
|
-
* coefficient of `x^k`. Polynomial and string inputs are converted
|
|
26
|
-
*
|
|
27
|
+
* coefficient of `x^k`. Expression, Polynomial, and string inputs are converted directly
|
|
28
|
+
* to numerical complex coefficients before solving. Linear and quadratic inputs are handled
|
|
27
29
|
* directly; higher degrees use simultaneous Aberth iteration followed by
|
|
28
30
|
* Newton refinement.
|
|
29
31
|
*
|
|
@@ -52,33 +54,73 @@ class PolynomialSolver {
|
|
|
52
54
|
/**
|
|
53
55
|
* Creates a polynomial root solver.
|
|
54
56
|
*
|
|
55
|
-
* @param input - A polynomial,
|
|
56
|
-
* coefficients.
|
|
57
|
+
* @param input - A polynomial expression, Polynomial, polynomial string, or ascending-power
|
|
58
|
+
* Decimal coefficients.
|
|
57
59
|
* @param variable - Variable expected in polynomial or string input. It is used to reject
|
|
58
60
|
* a conflicting variable or multivariate polynomial.
|
|
59
61
|
* @param precision - Working `decimal.js` precision used while finding roots.
|
|
60
62
|
* @param epsilon - Numerical convergence tolerance. Defaults to `1e-14`.
|
|
61
|
-
* @throws {@link core!UnexpectedInputError} If
|
|
62
|
-
*
|
|
63
|
+
* @throws {@link core!UnexpectedInputError} If symbolic input is multivariate, uses a
|
|
64
|
+
* variable different from `variable`, or has nonnumeric coefficients.
|
|
63
65
|
*/
|
|
64
66
|
constructor(input, variable, precision = DEFAULT_PRECISION, epsilon) {
|
|
65
|
-
|
|
66
|
-
if (
|
|
67
|
-
|
|
67
|
+
let coefficients;
|
|
68
|
+
if (Array.isArray(input)) {
|
|
69
|
+
coefficients = input.map(coefficient => new Complex_1.Complex(coefficient, 0));
|
|
68
70
|
}
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
else {
|
|
72
|
+
const polynomialInput = Polynomial_1.Polynomial.isPolynomial(input);
|
|
73
|
+
const expression = polynomialInput
|
|
74
|
+
? input.getExpression()
|
|
75
|
+
: typeof input === 'string'
|
|
76
|
+
? Expression_1.Expression.create(input)
|
|
77
|
+
: input;
|
|
78
|
+
const variables = polynomialInput ? input.variables : expression.variables();
|
|
79
|
+
if (!expression.isPolynomialLike()) {
|
|
80
|
+
throw new errors_1.PolynomialError((0, errors_1.message)('notAPolynomial'));
|
|
81
|
+
}
|
|
82
|
+
if (variables.length > 1 ||
|
|
83
|
+
(variables.length === 1 && variable && variables[0] !== variable)) {
|
|
73
84
|
throw new errors_1.UnexpectedInputError((0, errors_1.message)('tooManyUnknowns'));
|
|
74
85
|
}
|
|
86
|
+
const coefficientVariable = variable ?? variables[0];
|
|
87
|
+
const coefficientExpressions = coefficientVariable === undefined
|
|
88
|
+
? [expression]
|
|
89
|
+
: expression.coeffs(coefficientVariable).toArray();
|
|
90
|
+
const numericComponents = coefficientExpressions.map(coefficient => {
|
|
91
|
+
const real = coefficient.realPart();
|
|
92
|
+
const imaginary = coefficient.imagPart();
|
|
93
|
+
if (!real.isNUM() || !imaginary.isNUM()) {
|
|
94
|
+
throw new errors_1.UnexpectedInputError((0, errors_1.message)('wrongInput', {
|
|
95
|
+
expected: 'numeric polynomial coefficients',
|
|
96
|
+
received: coefficient.text(),
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
real: real.getMultiplier(),
|
|
101
|
+
imaginary: imaginary.getMultiplier(),
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
const exactRationals = numericComponents.every(component => !component.real.asDecimal && !component.imaginary.asDecimal);
|
|
105
|
+
if (exactRationals) {
|
|
106
|
+
const commonDenominator = (0, bigint_1.LCM)(...numericComponents.flatMap(component => [
|
|
107
|
+
component.real.denominator,
|
|
108
|
+
component.imaginary.denominator,
|
|
109
|
+
]));
|
|
110
|
+
coefficients = numericComponents.map(component => {
|
|
111
|
+
const real = component.real.numerator *
|
|
112
|
+
(commonDenominator / component.real.denominator);
|
|
113
|
+
const imaginary = component.imaginary.numerator *
|
|
114
|
+
(commonDenominator / component.imaginary.denominator);
|
|
115
|
+
return new Complex_1.Complex(new decimal_js_1.default(String(real)), new decimal_js_1.default(String(imaginary)));
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
coefficients = numericComponents.map(component => new Complex_1.Complex(component.real.toDecimal(), component.imaginary.toDecimal()));
|
|
120
|
+
}
|
|
75
121
|
}
|
|
76
|
-
const coefficients = Polynomial_1.Polynomial.isPolynomial(input) ? input.toDecimalArray() : input;
|
|
77
122
|
// Reverse the coefficients so p[0] is the leading coefficient
|
|
78
|
-
this.p = coefficients
|
|
79
|
-
.slice()
|
|
80
|
-
.reverse()
|
|
81
|
-
.map(c => new Complex_1.Complex(c, 0));
|
|
123
|
+
this.p = coefficients.slice().reverse();
|
|
82
124
|
this.degree = this.p.length - 1;
|
|
83
125
|
this.eps = epsilon || new decimal_js_1.default(DEFAULT_EPSILON);
|
|
84
126
|
this.precision = precision;
|
|
@@ -91,7 +133,7 @@ class PolynomialSolver {
|
|
|
91
133
|
const n = P.length - 1;
|
|
92
134
|
// Initialize roots on a circle
|
|
93
135
|
const roots = [];
|
|
94
|
-
const radius = this.
|
|
136
|
+
const radius = this.rootMagnitudeBound(P);
|
|
95
137
|
for (let k = 0; k < n; k++) {
|
|
96
138
|
const angle = new decimal_js_1.default(2).mul(Math.PI).mul(k).div(n).plus(ABERTH_INITIAL_ANGLE_OFFSET);
|
|
97
139
|
const r = radius
|
|
@@ -99,16 +141,18 @@ class PolynomialSolver {
|
|
|
99
141
|
.mul(new decimal_js_1.default(1).plus(new decimal_js_1.default(k).mul(ABERTH_ROOT_SPACING_SCALE).div(n)));
|
|
100
142
|
roots.push(new Complex_1.Complex(r.mul(decimal_js_1.default.cos(angle)), r.mul(decimal_js_1.default.sin(angle))));
|
|
101
143
|
}
|
|
144
|
+
const epsSquared = this.eps.mul(this.eps);
|
|
145
|
+
const convergenceTolerance = this.eps.mul(ABERTH_CONVERGENCE_SCALE);
|
|
146
|
+
const convergenceToleranceSquared = convergenceTolerance.mul(convergenceTolerance);
|
|
102
147
|
// Aberth iterations
|
|
103
148
|
for (let iter = 0; iter < ABERTH_MAX_ITERATIONS; iter++) {
|
|
104
|
-
let
|
|
149
|
+
let maxChangeSquared = new decimal_js_1.default(0);
|
|
105
150
|
const newRoots = [];
|
|
106
151
|
for (let i = 0; i < n; i++) {
|
|
107
152
|
const z = roots[i];
|
|
108
|
-
// Compute P(z) and P'(z)
|
|
109
|
-
const [pz,
|
|
110
|
-
|
|
111
|
-
if (dpz.abs().lessThan(this.eps)) {
|
|
153
|
+
// Compute P(z) and P'(z) together.
|
|
154
|
+
const [pz, dpz] = this.evaluateWithDerivative(P, z);
|
|
155
|
+
if (dpz.absSquared().lessThan(epsSquared)) {
|
|
112
156
|
newRoots.push(z);
|
|
113
157
|
continue;
|
|
114
158
|
}
|
|
@@ -119,28 +163,28 @@ class PolynomialSolver {
|
|
|
119
163
|
for (let j = 0; j < n; j++) {
|
|
120
164
|
if (i !== j) {
|
|
121
165
|
const diff = z.sub(roots[j]);
|
|
122
|
-
if (diff.
|
|
123
|
-
sum = sum.add(
|
|
166
|
+
if (diff.absSquared().greaterThan(epsSquared)) {
|
|
167
|
+
sum = sum.add(diff.reciprocal());
|
|
124
168
|
}
|
|
125
169
|
}
|
|
126
170
|
}
|
|
127
171
|
// Aberth correction: w = N(z) / (1 - N(z) * sum)
|
|
128
172
|
const denom = new Complex_1.Complex(1, 0).sub(newton.mul(sum));
|
|
129
|
-
const correction = denom.
|
|
173
|
+
const correction = denom.absSquared().lessThan(epsSquared)
|
|
130
174
|
? newton
|
|
131
175
|
: newton.div(denom);
|
|
132
176
|
const newZ = z.sub(correction);
|
|
133
177
|
newRoots.push(newZ);
|
|
134
|
-
const
|
|
135
|
-
if (
|
|
136
|
-
|
|
178
|
+
const changeSquared = correction.absSquared();
|
|
179
|
+
if (changeSquared.greaterThan(maxChangeSquared)) {
|
|
180
|
+
maxChangeSquared = changeSquared;
|
|
137
181
|
}
|
|
138
182
|
}
|
|
139
183
|
for (let i = 0; i < n; i++) {
|
|
140
184
|
roots[i] = newRoots[i];
|
|
141
185
|
}
|
|
142
186
|
// Check convergence
|
|
143
|
-
if (
|
|
187
|
+
if (maxChangeSquared.lessThan(convergenceToleranceSquared)) {
|
|
144
188
|
break;
|
|
145
189
|
}
|
|
146
190
|
}
|
|
@@ -150,20 +194,6 @@ class PolynomialSolver {
|
|
|
150
194
|
}
|
|
151
195
|
return roots;
|
|
152
196
|
}
|
|
153
|
-
/**
|
|
154
|
-
* Compute Cauchy bound for root magnitudes
|
|
155
|
-
*/
|
|
156
|
-
cauchyBound(P) {
|
|
157
|
-
const n = P.length - 1;
|
|
158
|
-
let maxRatio = new decimal_js_1.default(0);
|
|
159
|
-
for (let i = 1; i <= n; i++) {
|
|
160
|
-
const ratio = P[i].abs().div(P[0].abs());
|
|
161
|
-
if (ratio.greaterThan(maxRatio)) {
|
|
162
|
-
maxRatio = ratio;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
return maxRatio.plus(1);
|
|
166
|
-
}
|
|
167
197
|
complexSqrt(z) {
|
|
168
198
|
const mag = z.abs();
|
|
169
199
|
if (mag.isZero()) {
|
|
@@ -189,6 +219,20 @@ class PolynomialSolver {
|
|
|
189
219
|
return new Complex_1.Complex(absY.div(w.mul(2)), imPart);
|
|
190
220
|
}
|
|
191
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* Evaluates a polynomial and its derivative together using Horner's method.
|
|
224
|
+
*/
|
|
225
|
+
evaluateWithDerivative(P, z) {
|
|
226
|
+
const n = P.length - 1;
|
|
227
|
+
let value = P[0];
|
|
228
|
+
let derivative = P[0];
|
|
229
|
+
for (let i = 1; i < n; i++) {
|
|
230
|
+
value = value.mul(z).add(P[i]);
|
|
231
|
+
derivative = derivative.mul(z).add(value);
|
|
232
|
+
}
|
|
233
|
+
value = value.mul(z).add(P[n]);
|
|
234
|
+
return [value, derivative];
|
|
235
|
+
}
|
|
192
236
|
finalize(roots, asExpressions, precision, cleanupComponents = true) {
|
|
193
237
|
// Restore the precision
|
|
194
238
|
decimal_js_1.default.set({ precision: precision });
|
|
@@ -208,22 +252,154 @@ class PolynomialSolver {
|
|
|
208
252
|
}
|
|
209
253
|
return roots;
|
|
210
254
|
}
|
|
255
|
+
isNumericalRoot(root) {
|
|
256
|
+
let residual = this.p[0];
|
|
257
|
+
let scale = this.p[0].abs();
|
|
258
|
+
const magnitude = root.abs();
|
|
259
|
+
for (let i = 1; i < this.p.length; i++) {
|
|
260
|
+
residual = residual.mul(root).add(this.p[i]);
|
|
261
|
+
scale = scale.mul(magnitude).plus(this.p[i].abs());
|
|
262
|
+
}
|
|
263
|
+
const tolerance = this.eps.mul(scale.plus(1));
|
|
264
|
+
return residual.absSquared().lte(tolerance.mul(tolerance));
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Uses native double-precision arithmetic to locate starting points for Decimal refinement.
|
|
268
|
+
*
|
|
269
|
+
* Returning `null` leaves the existing Decimal Aberth implementation responsible for the
|
|
270
|
+
* complete solve.
|
|
271
|
+
*/
|
|
272
|
+
nativeAberthSeeds(P) {
|
|
273
|
+
const n = P.length - 1;
|
|
274
|
+
const nativeP = [];
|
|
275
|
+
for (const coefficient of P) {
|
|
276
|
+
const re = coefficient.re.toNumber();
|
|
277
|
+
const im = coefficient.im.toNumber();
|
|
278
|
+
if (!Number.isFinite(re) || !Number.isFinite(im)) {
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
nativeP.push({ re, im });
|
|
282
|
+
}
|
|
283
|
+
const leadMagnitude = Math.hypot(nativeP[0].re, nativeP[0].im);
|
|
284
|
+
if (!Number.isFinite(leadMagnitude) || leadMagnitude === 0) {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
const radius = this.rootMagnitudeBound(P).toNumber();
|
|
288
|
+
if (!Number.isFinite(radius)) {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
const roots = [];
|
|
292
|
+
for (let k = 0; k < n; k++) {
|
|
293
|
+
const angle = (2 * Math.PI * k) / n + ABERTH_INITIAL_ANGLE_OFFSET;
|
|
294
|
+
const r = radius *
|
|
295
|
+
ABERTH_RADIUS_SCALE *
|
|
296
|
+
(1 + (k * ABERTH_ROOT_SPACING_SCALE) / n);
|
|
297
|
+
roots.push({ re: r * Math.cos(angle), im: r * Math.sin(angle) });
|
|
298
|
+
}
|
|
299
|
+
const nativeTolerance = Math.max(this.eps.toNumber(), Number.EPSILON * NATIVE_EPSILON_SCALE);
|
|
300
|
+
const epsSquared = nativeTolerance * nativeTolerance;
|
|
301
|
+
for (let iter = 0; iter < ABERTH_MAX_ITERATIONS; iter++) {
|
|
302
|
+
let maxChangeSquared = 0;
|
|
303
|
+
const newRoots = [];
|
|
304
|
+
for (let i = 0; i < n; i++) {
|
|
305
|
+
const z = roots[i];
|
|
306
|
+
let valueRe = nativeP[0].re;
|
|
307
|
+
let valueIm = nativeP[0].im;
|
|
308
|
+
let derivativeRe = nativeP[0].re;
|
|
309
|
+
let derivativeIm = nativeP[0].im;
|
|
310
|
+
for (let k = 1; k < n; k++) {
|
|
311
|
+
const nextValueRe = valueRe * z.re - valueIm * z.im + nativeP[k].re;
|
|
312
|
+
const nextValueIm = valueRe * z.im + valueIm * z.re + nativeP[k].im;
|
|
313
|
+
const nextDerivativeRe = derivativeRe * z.re - derivativeIm * z.im + nextValueRe;
|
|
314
|
+
const nextDerivativeIm = derivativeRe * z.im + derivativeIm * z.re + nextValueIm;
|
|
315
|
+
valueRe = nextValueRe;
|
|
316
|
+
valueIm = nextValueIm;
|
|
317
|
+
derivativeRe = nextDerivativeRe;
|
|
318
|
+
derivativeIm = nextDerivativeIm;
|
|
319
|
+
}
|
|
320
|
+
const finalValueRe = valueRe * z.re - valueIm * z.im + nativeP[n].re;
|
|
321
|
+
const finalValueIm = valueRe * z.im + valueIm * z.re + nativeP[n].im;
|
|
322
|
+
const derivativeMagnitudeSquared = derivativeRe * derivativeRe + derivativeIm * derivativeIm;
|
|
323
|
+
if (!Number.isFinite(finalValueRe) ||
|
|
324
|
+
!Number.isFinite(finalValueIm) ||
|
|
325
|
+
!Number.isFinite(derivativeMagnitudeSquared)) {
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
if (derivativeMagnitudeSquared < epsSquared) {
|
|
329
|
+
newRoots.push(z);
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
const newtonRe = (finalValueRe * derivativeRe + finalValueIm * derivativeIm) /
|
|
333
|
+
derivativeMagnitudeSquared;
|
|
334
|
+
const newtonIm = (finalValueIm * derivativeRe - finalValueRe * derivativeIm) /
|
|
335
|
+
derivativeMagnitudeSquared;
|
|
336
|
+
let sumRe = 0;
|
|
337
|
+
let sumIm = 0;
|
|
338
|
+
for (let j = 0; j < n; j++) {
|
|
339
|
+
if (i === j) {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
const diffRe = z.re - roots[j].re;
|
|
343
|
+
const diffIm = z.im - roots[j].im;
|
|
344
|
+
const diffMagnitudeSquared = diffRe * diffRe + diffIm * diffIm;
|
|
345
|
+
if (diffMagnitudeSquared > epsSquared) {
|
|
346
|
+
sumRe += diffRe / diffMagnitudeSquared;
|
|
347
|
+
sumIm -= diffIm / diffMagnitudeSquared;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
const productRe = newtonRe * sumRe - newtonIm * sumIm;
|
|
351
|
+
const productIm = newtonRe * sumIm + newtonIm * sumRe;
|
|
352
|
+
const denomRe = 1 - productRe;
|
|
353
|
+
const denomIm = -productIm;
|
|
354
|
+
const denomMagnitudeSquared = denomRe * denomRe + denomIm * denomIm;
|
|
355
|
+
let correctionRe = newtonRe;
|
|
356
|
+
let correctionIm = newtonIm;
|
|
357
|
+
if (denomMagnitudeSquared >= epsSquared) {
|
|
358
|
+
correctionRe =
|
|
359
|
+
(newtonRe * denomRe + newtonIm * denomIm) / denomMagnitudeSquared;
|
|
360
|
+
correctionIm =
|
|
361
|
+
(newtonIm * denomRe - newtonRe * denomIm) / denomMagnitudeSquared;
|
|
362
|
+
}
|
|
363
|
+
const newRoot = {
|
|
364
|
+
re: z.re - correctionRe,
|
|
365
|
+
im: z.im - correctionIm,
|
|
366
|
+
};
|
|
367
|
+
const changeSquared = correctionRe * correctionRe + correctionIm * correctionIm;
|
|
368
|
+
if (!Number.isFinite(newRoot.re) ||
|
|
369
|
+
!Number.isFinite(newRoot.im) ||
|
|
370
|
+
!Number.isFinite(changeSquared)) {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
newRoots.push(newRoot);
|
|
374
|
+
maxChangeSquared = Math.max(maxChangeSquared, changeSquared);
|
|
375
|
+
}
|
|
376
|
+
for (let i = 0; i < n; i++) {
|
|
377
|
+
roots[i] = newRoots[i];
|
|
378
|
+
}
|
|
379
|
+
if (maxChangeSquared < epsSquared) {
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return roots.map(root => new Complex_1.Complex(new decimal_js_1.default(root.re), new decimal_js_1.default(root.im)));
|
|
384
|
+
}
|
|
211
385
|
/**
|
|
212
386
|
* Refine a single root using Newton's method
|
|
213
387
|
*/
|
|
214
388
|
newtonRefine(P, z) {
|
|
389
|
+
const epsSquared = this.eps.mul(this.eps);
|
|
390
|
+
const convergenceTolerance = this.eps.mul(NEWTON_CONVERGENCE_SCALE);
|
|
391
|
+
const convergenceToleranceSquared = convergenceTolerance.mul(convergenceTolerance);
|
|
215
392
|
for (let iter = 0; iter < NEWTON_MAX_ITERATIONS; iter++) {
|
|
216
|
-
const [pz,
|
|
217
|
-
if (pz.
|
|
393
|
+
const [pz, dpz] = this.evaluateWithDerivative(P, z);
|
|
394
|
+
if (pz.absSquared().lessThan(convergenceToleranceSquared)) {
|
|
218
395
|
break;
|
|
219
396
|
}
|
|
220
|
-
|
|
221
|
-
if (dpz.abs().lessThan(this.eps)) {
|
|
397
|
+
if (dpz.absSquared().lessThan(epsSquared)) {
|
|
222
398
|
break;
|
|
223
399
|
}
|
|
224
400
|
const correction = pz.div(dpz);
|
|
225
401
|
const newZ = z.sub(correction);
|
|
226
|
-
if (correction.abs().lessThan(
|
|
402
|
+
if (correction.abs().lessThan(convergenceTolerance.mul(newZ.abs().plus(1)))) {
|
|
227
403
|
z = newZ;
|
|
228
404
|
break;
|
|
229
405
|
}
|
|
@@ -231,6 +407,29 @@ class PolynomialSolver {
|
|
|
231
407
|
}
|
|
232
408
|
return z;
|
|
233
409
|
}
|
|
410
|
+
/**
|
|
411
|
+
* Computes a Lagrange-Fujiwara upper bound for root magnitudes.
|
|
412
|
+
*
|
|
413
|
+
* For a polynomial with leading coefficient a_n, every root lies within
|
|
414
|
+
* 2 * max(|a_(n-i) / a_n|^(1/i)). This is substantially tighter than the
|
|
415
|
+
* simple Cauchy bound for polynomials with large middle coefficients.
|
|
416
|
+
*
|
|
417
|
+
* @see https://doi.org/10.1016/S0377-0427(03)00381-9
|
|
418
|
+
*/
|
|
419
|
+
rootMagnitudeBound(P) {
|
|
420
|
+
let maxCandidate = new decimal_js_1.default(0);
|
|
421
|
+
const leadingMagnitude = P[0].abs();
|
|
422
|
+
for (let i = 1; i < P.length; i++) {
|
|
423
|
+
const ratio = P[i].abs().div(leadingMagnitude);
|
|
424
|
+
if (!ratio.isZero()) {
|
|
425
|
+
const candidate = ratio.pow(new decimal_js_1.default(1).div(i));
|
|
426
|
+
if (candidate.greaterThan(maxCandidate)) {
|
|
427
|
+
maxCandidate = candidate;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return maxCandidate.mul(2);
|
|
432
|
+
}
|
|
234
433
|
solveQuadratic(p) {
|
|
235
434
|
const a = p[0];
|
|
236
435
|
const b = p[1];
|
|
@@ -243,22 +442,6 @@ class PolynomialSolver {
|
|
|
243
442
|
const negB = b.neg();
|
|
244
443
|
return [negB.add(sqrtDisc).div(twoA), negB.sub(sqrtDisc).div(twoA)];
|
|
245
444
|
}
|
|
246
|
-
/**
|
|
247
|
-
* Synthetic division
|
|
248
|
-
* Returns [P(s), quotient Q] where P(x) = (x-s)*Q(x) + P(s)
|
|
249
|
-
*/
|
|
250
|
-
syntheticDiv(n, P, s) {
|
|
251
|
-
const Q = [];
|
|
252
|
-
let b = P[0];
|
|
253
|
-
Q[0] = b;
|
|
254
|
-
for (let i = 1; i <= n; i++) {
|
|
255
|
-
b = b.mul(s).add(P[i]);
|
|
256
|
-
if (i < n) {
|
|
257
|
-
Q[i] = b;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
return [b, Q];
|
|
261
|
-
}
|
|
262
445
|
roots(asExpressions = true) {
|
|
263
446
|
// Store the currently used precision
|
|
264
447
|
const precision = decimal_js_1.default.precision;
|
|
@@ -284,9 +467,26 @@ class PolynomialSolver {
|
|
|
284
467
|
// treating them as iterative noise.
|
|
285
468
|
return this.finalize(this.solveQuadratic(poly), asExpressions, precision, false);
|
|
286
469
|
}
|
|
287
|
-
//
|
|
470
|
+
// Native doubles are sufficient to locate ordinary roots. Refine those seeds with
|
|
471
|
+
// Decimal arithmetic and accept them only when every root passes the existing residual check.
|
|
472
|
+
const nativeSeeds = this.nativeAberthSeeds(poly);
|
|
473
|
+
if (nativeSeeds) {
|
|
474
|
+
const nativeRoots = nativeSeeds.map(root => this.newtonRefine(poly, root));
|
|
475
|
+
if (nativeRoots.length === deg &&
|
|
476
|
+
nativeRoots.every(root => this.isNumericalRoot(root))) {
|
|
477
|
+
return this.finalize(nativeRoots, asExpressions, precision);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
// Fall back to the Decimal Aberth implementation when native seeding is unavailable
|
|
481
|
+
// or does not survive Decimal refinement and validation.
|
|
288
482
|
const roots = this.aberthMethod(poly);
|
|
289
483
|
return this.finalize(roots, asExpressions, precision);
|
|
290
484
|
}
|
|
485
|
+
validatedRoots(asExpressions = true) {
|
|
486
|
+
const precision = decimal_js_1.default.precision;
|
|
487
|
+
decimal_js_1.default.set({ precision: this.precision });
|
|
488
|
+
const roots = this.roots(false).filter(root => this.isNumericalRoot(root));
|
|
489
|
+
return this.finalize(roots, asExpressions, precision);
|
|
490
|
+
}
|
|
291
491
|
}
|
|
292
492
|
exports.PolynomialSolver = PolynomialSolver;
|