document-compute.js 1.5.8 → 1.5.9
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/dist/index.cjs +20 -30
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +20 -31
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -11,20 +11,17 @@ function toRational(value) {
|
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
13
|
function gcd(a, b) {
|
|
14
|
-
let x =
|
|
15
|
-
let y = b;
|
|
14
|
+
let x = b;
|
|
15
|
+
let y = (a % b + b) % b;
|
|
16
16
|
while (y !== 0n) [x, y] = [y, x % y];
|
|
17
17
|
return x;
|
|
18
18
|
}
|
|
19
19
|
function reduce(n, d) {
|
|
20
20
|
if (d === 0n) throw new RangeError("rational.ts: denominator must not be zero");
|
|
21
|
-
const
|
|
22
|
-
const num = n * sign;
|
|
23
|
-
const den = d * sign;
|
|
24
|
-
const g = gcd(num, den);
|
|
21
|
+
const g = gcd(n, d);
|
|
25
22
|
return {
|
|
26
|
-
n:
|
|
27
|
-
d:
|
|
23
|
+
n: n / g,
|
|
24
|
+
d: d / g
|
|
28
25
|
};
|
|
29
26
|
}
|
|
30
27
|
function toExactRational(value) {
|
|
@@ -267,9 +264,7 @@ function negateInterval(a) {
|
|
|
267
264
|
return interval(-a.max, -a.min, a.dimension);
|
|
268
265
|
}
|
|
269
266
|
function absInterval(a) {
|
|
270
|
-
|
|
271
|
-
if (a.max <= 0) return negateInterval(a);
|
|
272
|
-
return interval(0, Math.max(-a.min, a.max), a.dimension);
|
|
267
|
+
return interval(Math.max(0, a.min, -a.max), Math.max(-a.min, a.max), a.dimension);
|
|
273
268
|
}
|
|
274
269
|
//#endregion
|
|
275
270
|
//#region src/compute/evaluate.ts
|
|
@@ -283,7 +278,7 @@ function isInterval(value) {
|
|
|
283
278
|
function toInterval(value) {
|
|
284
279
|
return isInterval(value) ? value : pointInterval(value.magnitude, value.dimension);
|
|
285
280
|
}
|
|
286
|
-
function asQuantity
|
|
281
|
+
function asQuantity(value, context) {
|
|
287
282
|
if (isInterval(value)) throw new UnsupportedExpressionError(context, "this position requires a plain Quantity, not an Interval");
|
|
288
283
|
return value;
|
|
289
284
|
}
|
|
@@ -335,6 +330,9 @@ function evaluate(expression, bindings, context = EMPTY_SYMBOL_TABLE$2) {
|
|
|
335
330
|
case "unparsed": throw new UnsupportedExpressionError("evaluate", `this node is source LaTeX ("${expression.latex}") document-schema.js's lowering could not represent structurally, so there is nothing to evaluate`);
|
|
336
331
|
}
|
|
337
332
|
}
|
|
333
|
+
function evaluateQuantity(expression, bindings, context = EMPTY_SYMBOL_TABLE$2) {
|
|
334
|
+
return asQuantity(evaluate(expression, bindings, context), "evaluateQuantity");
|
|
335
|
+
}
|
|
338
336
|
function evaluateQty(node, context) {
|
|
339
337
|
const unit = context.units.find((entry) => entry.id === node.unit);
|
|
340
338
|
if (unit === void 0) throw new UnknownUnitError(node.unit);
|
|
@@ -371,13 +369,13 @@ function evaluateApp(node, bindings, context) {
|
|
|
371
369
|
}
|
|
372
370
|
if (node.operator === "math:pow") {
|
|
373
371
|
const [base, exponent] = expectTwoArgs(args, "'math:pow'");
|
|
374
|
-
return powQuantity(asQuantity
|
|
372
|
+
return powQuantity(asQuantity(base, "evaluate"), asQuantity(exponent, "evaluate"));
|
|
375
373
|
}
|
|
376
374
|
throw new UnsupportedExpressionError("evaluate", `unknown operator '${node.operator}'`);
|
|
377
375
|
}
|
|
378
376
|
function evaluateBinder(node, bindings, context) {
|
|
379
|
-
const lower = asQuantity
|
|
380
|
-
const upper = asQuantity
|
|
377
|
+
const lower = asQuantity(evaluate(node.lower, bindings, context), `evaluate:${node.kind}`);
|
|
378
|
+
const upper = asQuantity(evaluate(node.upper, bindings, context), `evaluate:${node.kind}`);
|
|
381
379
|
if (!isDimensionless(lower.dimension) || !isDimensionless(upper.dimension)) throw new IncompatibleDimensionsError(`math:${node.kind}`, lower.dimension, upper.dimension, "binder bounds must be dimensionless");
|
|
382
380
|
if (!Number.isInteger(lower.magnitude) || !Number.isInteger(upper.magnitude)) throw new UnsupportedExpressionError(`evaluate:${node.kind}`, "binder bounds must evaluate to integers");
|
|
383
381
|
let accumulator = node.kind === "sum" ? quantity(0, {}) : quantity(1, {});
|
|
@@ -386,7 +384,7 @@ function evaluateBinder(node, bindings, context) {
|
|
|
386
384
|
...bindings,
|
|
387
385
|
[node.binder]: quantity(i, {})
|
|
388
386
|
};
|
|
389
|
-
const bodyValue = asQuantity
|
|
387
|
+
const bodyValue = asQuantity(evaluate(node.body, bodyBindings, context), `evaluate:${node.kind}`);
|
|
390
388
|
accumulator = node.kind === "sum" ? addQuantities(accumulator, bodyValue) : multiplyQuantities(accumulator, bodyValue);
|
|
391
389
|
}
|
|
392
390
|
return accumulator;
|
|
@@ -430,7 +428,7 @@ function bisection(f, bracket, tolerance, maxIterations) {
|
|
|
430
428
|
const mid = (low + high) / 2;
|
|
431
429
|
const fMid = f(mid);
|
|
432
430
|
if (Math.abs(fMid) < tolerance) return mid;
|
|
433
|
-
if (fMid
|
|
431
|
+
if (Math.sign(fMid) === Math.sign(fLow)) {
|
|
434
432
|
low = mid;
|
|
435
433
|
fLow = fMid;
|
|
436
434
|
} else high = mid;
|
|
@@ -445,9 +443,7 @@ function newton(f, initialGuess, tolerance, maxIterations, h) {
|
|
|
445
443
|
if (Math.abs(fx) < tolerance) return x;
|
|
446
444
|
const derivative = (f(x + h) - f(x - h)) / (2 * h);
|
|
447
445
|
if (!Number.isFinite(derivative) || Math.abs(derivative) < 1e-14) throw new NonConvergentSolveError("newton", i, `the numeric derivative vanished or diverged near x=${x}`);
|
|
448
|
-
|
|
449
|
-
if (!Number.isFinite(next)) throw new NonConvergentSolveError("newton", i, `the iteration diverged to a non-finite value near x=${x}`);
|
|
450
|
-
x = next;
|
|
446
|
+
x = x - fx / derivative;
|
|
451
447
|
}
|
|
452
448
|
throw new NonConvergentSolveError("newton", maxIterations, `residual still exceeds tolerance ${tolerance} after ${maxIterations} iterations`);
|
|
453
449
|
}
|
|
@@ -471,19 +467,13 @@ function asEquality(expression) {
|
|
|
471
467
|
function containsSymbol(expression) {
|
|
472
468
|
switch (expression.kind) {
|
|
473
469
|
case "sym": return true;
|
|
474
|
-
case "num":
|
|
475
|
-
case "qty":
|
|
476
|
-
case "unparsed": return false;
|
|
477
470
|
case "app": return expression.args.some(containsSymbol);
|
|
478
471
|
case "sum":
|
|
479
472
|
case "prod": return containsSymbol(expression.lower) || containsSymbol(expression.upper) || containsSymbol(expression.body);
|
|
480
473
|
case "matrix": return expression.rows.some((row) => row.some(containsSymbol));
|
|
474
|
+
default: return false;
|
|
481
475
|
}
|
|
482
476
|
}
|
|
483
|
-
function asQuantity(value) {
|
|
484
|
-
if (isInterval(value)) throw new UnsupportedExpressionError("runWorkedExampleSequence", "this harness compares point-valued Quantity answers only; a symbol resolving to a range (Interval) has no stated-answer comparison defined yet");
|
|
485
|
-
return value;
|
|
486
|
-
}
|
|
487
477
|
function gapFromError(error) {
|
|
488
478
|
if (error instanceof UnboundSymbolError) return "unbound-symbol";
|
|
489
479
|
if (error instanceof UnknownUnitError) return "unknown-unit";
|
|
@@ -491,7 +481,6 @@ function gapFromError(error) {
|
|
|
491
481
|
if (error instanceof DivisionByZeroError) return "division-by-zero";
|
|
492
482
|
if (error instanceof UnsupportedExpressionError) return "unsupported-construct";
|
|
493
483
|
if (error instanceof NumericDomainError) return "numeric-domain";
|
|
494
|
-
if (error instanceof NonConvergentSolveError) return "non-convergent-solve";
|
|
495
484
|
return "other-evaluation-error";
|
|
496
485
|
}
|
|
497
486
|
function errorMessage(error) {
|
|
@@ -533,7 +522,7 @@ function runWorkedExampleSequence(formulas, symbolTable = EMPTY_SYMBOL_TABLE, op
|
|
|
533
522
|
}
|
|
534
523
|
let closedValue;
|
|
535
524
|
try {
|
|
536
|
-
closedValue =
|
|
525
|
+
closedValue = evaluateQuantity(rhs, EMPTY_BINDINGS, symbolTable);
|
|
537
526
|
} catch (error) {
|
|
538
527
|
outcomes.push({
|
|
539
528
|
outcome: "gap",
|
|
@@ -548,7 +537,7 @@ function runWorkedExampleSequence(formulas, symbolTable = EMPTY_SYMBOL_TABLE, op
|
|
|
548
537
|
pending = void 0;
|
|
549
538
|
let actual;
|
|
550
539
|
try {
|
|
551
|
-
actual =
|
|
540
|
+
actual = evaluateQuantity(definitionRhs, bindings, symbolTable);
|
|
552
541
|
} catch (error) {
|
|
553
542
|
outcomes.push({
|
|
554
543
|
outcome: "gap",
|
|
@@ -680,6 +669,7 @@ exports.divideIntervals = divideIntervals;
|
|
|
680
669
|
exports.divideQuantities = divideQuantities;
|
|
681
670
|
exports.divideRational = divideRational;
|
|
682
671
|
exports.evaluate = evaluate;
|
|
672
|
+
exports.evaluateQuantity = evaluateQuantity;
|
|
683
673
|
exports.formatCorpusReport = formatCorpusReport;
|
|
684
674
|
exports.interval = interval;
|
|
685
675
|
exports.isDimensionless = isDimensionless;
|
package/dist/index.d.cts
CHANGED
|
@@ -82,6 +82,7 @@ declare function absInterval(a: Interval): Interval;
|
|
|
82
82
|
type EvaluationResult = Quantity | Interval;
|
|
83
83
|
declare function isInterval(value: EvaluationResult): value is Interval;
|
|
84
84
|
declare function evaluate(expression: MathExpression, bindings: FormulaBindings, context?: SymbolTable): EvaluationResult;
|
|
85
|
+
declare function evaluateQuantity(expression: MathExpression, bindings: FormulaBindings, context?: SymbolTable): Quantity;
|
|
85
86
|
//#endregion
|
|
86
87
|
//#region src/compute/solve.d.ts
|
|
87
88
|
type SolveMethod = "bisection" | "newton";
|
|
@@ -104,7 +105,7 @@ interface SolveForOptions {
|
|
|
104
105
|
declare function solveFor(expression: MathExpression, targetValue: number, unknownSymbol: string, bindings: FormulaBindings, options?: SolveForOptions, context?: SymbolTable): number;
|
|
105
106
|
//#endregion
|
|
106
107
|
//#region src/harness/worked-example.d.ts
|
|
107
|
-
type WorkedExampleGap = "unbound-symbol" | "unknown-unit" | "incompatible-dimensions" | "division-by-zero" | "unsupported-construct" | "numeric-domain" | "
|
|
108
|
+
type WorkedExampleGap = "unbound-symbol" | "unknown-unit" | "incompatible-dimensions" | "division-by-zero" | "unsupported-construct" | "numeric-domain" | "other-evaluation-error";
|
|
108
109
|
interface WorkedExampleMatch {
|
|
109
110
|
readonly outcome: "match";
|
|
110
111
|
readonly targetSymbol: string;
|
|
@@ -165,4 +166,4 @@ interface CorpusReport {
|
|
|
165
166
|
declare function runCorpus(documents: readonly CorpusDocument[], options?: WorkedExampleOptions): CorpusReport;
|
|
166
167
|
declare function formatCorpusReport(report: CorpusReport): string;
|
|
167
168
|
//#endregion
|
|
168
|
-
export { CorpusDocument, CorpusDocumentReport, CorpusReport, DivisionByZeroError, EvaluationResult, IncompatibleDimensionsError, NonConvergentSolveError, NumericDomainError, Rational, SolveForOptions, SolveMethod, UnboundSymbolError, UnknownUnitError, UnsupportedExpressionError, WorkedExampleGap, WorkedExampleGapResult, WorkedExampleMatch, WorkedExampleMismatch, WorkedExampleOptions, WorkedExampleOutcome, WorkedExampleReport, WorkedExampleUnresolved, absInterval, absQuantity, addIntervals, addQuantities, addRational, collectFormulas, cosQuantity, dimensionExponent, dimensionToString, dimensionsEqual, divideDimensions, divideIntervals, divideQuantities, divideRational, evaluate, formatCorpusReport, interval, isDimensionless, isInterval, multiplyDimensions, multiplyIntervals, multiplyQuantities, multiplyRational, negateInterval, negateQuantity, pointInterval, powQuantity, quantity, rationalToNumber, runCorpus, runWorkedExampleSequence, scaleDimension, sinQuantity, solveFor, sqrtQuantity, subtractIntervals, subtractQuantities, subtractRational, tanQuantity, toExactRational, toRational };
|
|
169
|
+
export { CorpusDocument, CorpusDocumentReport, CorpusReport, DivisionByZeroError, EvaluationResult, IncompatibleDimensionsError, NonConvergentSolveError, NumericDomainError, Rational, SolveForOptions, SolveMethod, UnboundSymbolError, UnknownUnitError, UnsupportedExpressionError, WorkedExampleGap, WorkedExampleGapResult, WorkedExampleMatch, WorkedExampleMismatch, WorkedExampleOptions, WorkedExampleOutcome, WorkedExampleReport, WorkedExampleUnresolved, absInterval, absQuantity, addIntervals, addQuantities, addRational, collectFormulas, cosQuantity, dimensionExponent, dimensionToString, dimensionsEqual, divideDimensions, divideIntervals, divideQuantities, divideRational, evaluate, evaluateQuantity, formatCorpusReport, interval, isDimensionless, isInterval, multiplyDimensions, multiplyIntervals, multiplyQuantities, multiplyRational, negateInterval, negateQuantity, pointInterval, powQuantity, quantity, rationalToNumber, runCorpus, runWorkedExampleSequence, scaleDimension, sinQuantity, solveFor, sqrtQuantity, subtractIntervals, subtractQuantities, subtractRational, tanQuantity, toExactRational, toRational };
|
package/dist/index.d.ts
CHANGED
|
@@ -82,6 +82,7 @@ declare function absInterval(a: Interval): Interval;
|
|
|
82
82
|
type EvaluationResult = Quantity | Interval;
|
|
83
83
|
declare function isInterval(value: EvaluationResult): value is Interval;
|
|
84
84
|
declare function evaluate(expression: MathExpression, bindings: FormulaBindings, context?: SymbolTable): EvaluationResult;
|
|
85
|
+
declare function evaluateQuantity(expression: MathExpression, bindings: FormulaBindings, context?: SymbolTable): Quantity;
|
|
85
86
|
//#endregion
|
|
86
87
|
//#region src/compute/solve.d.ts
|
|
87
88
|
type SolveMethod = "bisection" | "newton";
|
|
@@ -104,7 +105,7 @@ interface SolveForOptions {
|
|
|
104
105
|
declare function solveFor(expression: MathExpression, targetValue: number, unknownSymbol: string, bindings: FormulaBindings, options?: SolveForOptions, context?: SymbolTable): number;
|
|
105
106
|
//#endregion
|
|
106
107
|
//#region src/harness/worked-example.d.ts
|
|
107
|
-
type WorkedExampleGap = "unbound-symbol" | "unknown-unit" | "incompatible-dimensions" | "division-by-zero" | "unsupported-construct" | "numeric-domain" | "
|
|
108
|
+
type WorkedExampleGap = "unbound-symbol" | "unknown-unit" | "incompatible-dimensions" | "division-by-zero" | "unsupported-construct" | "numeric-domain" | "other-evaluation-error";
|
|
108
109
|
interface WorkedExampleMatch {
|
|
109
110
|
readonly outcome: "match";
|
|
110
111
|
readonly targetSymbol: string;
|
|
@@ -165,4 +166,4 @@ interface CorpusReport {
|
|
|
165
166
|
declare function runCorpus(documents: readonly CorpusDocument[], options?: WorkedExampleOptions): CorpusReport;
|
|
166
167
|
declare function formatCorpusReport(report: CorpusReport): string;
|
|
167
168
|
//#endregion
|
|
168
|
-
export { CorpusDocument, CorpusDocumentReport, CorpusReport, DivisionByZeroError, EvaluationResult, IncompatibleDimensionsError, NonConvergentSolveError, NumericDomainError, Rational, SolveForOptions, SolveMethod, UnboundSymbolError, UnknownUnitError, UnsupportedExpressionError, WorkedExampleGap, WorkedExampleGapResult, WorkedExampleMatch, WorkedExampleMismatch, WorkedExampleOptions, WorkedExampleOutcome, WorkedExampleReport, WorkedExampleUnresolved, absInterval, absQuantity, addIntervals, addQuantities, addRational, collectFormulas, cosQuantity, dimensionExponent, dimensionToString, dimensionsEqual, divideDimensions, divideIntervals, divideQuantities, divideRational, evaluate, formatCorpusReport, interval, isDimensionless, isInterval, multiplyDimensions, multiplyIntervals, multiplyQuantities, multiplyRational, negateInterval, negateQuantity, pointInterval, powQuantity, quantity, rationalToNumber, runCorpus, runWorkedExampleSequence, scaleDimension, sinQuantity, solveFor, sqrtQuantity, subtractIntervals, subtractQuantities, subtractRational, tanQuantity, toExactRational, toRational };
|
|
169
|
+
export { CorpusDocument, CorpusDocumentReport, CorpusReport, DivisionByZeroError, EvaluationResult, IncompatibleDimensionsError, NonConvergentSolveError, NumericDomainError, Rational, SolveForOptions, SolveMethod, UnboundSymbolError, UnknownUnitError, UnsupportedExpressionError, WorkedExampleGap, WorkedExampleGapResult, WorkedExampleMatch, WorkedExampleMismatch, WorkedExampleOptions, WorkedExampleOutcome, WorkedExampleReport, WorkedExampleUnresolved, absInterval, absQuantity, addIntervals, addQuantities, addRational, collectFormulas, cosQuantity, dimensionExponent, dimensionToString, dimensionsEqual, divideDimensions, divideIntervals, divideQuantities, divideRational, evaluate, evaluateQuantity, formatCorpusReport, interval, isDimensionless, isInterval, multiplyDimensions, multiplyIntervals, multiplyQuantities, multiplyRational, negateInterval, negateQuantity, pointInterval, powQuantity, quantity, rationalToNumber, runCorpus, runWorkedExampleSequence, scaleDimension, sinQuantity, solveFor, sqrtQuantity, subtractIntervals, subtractQuantities, subtractRational, tanQuantity, toExactRational, toRational };
|
package/dist/index.js
CHANGED
|
@@ -10,20 +10,17 @@ function toRational(value) {
|
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
12
|
function gcd(a, b) {
|
|
13
|
-
let x =
|
|
14
|
-
let y = b;
|
|
13
|
+
let x = b;
|
|
14
|
+
let y = (a % b + b) % b;
|
|
15
15
|
while (y !== 0n) [x, y] = [y, x % y];
|
|
16
16
|
return x;
|
|
17
17
|
}
|
|
18
18
|
function reduce(n, d) {
|
|
19
19
|
if (d === 0n) throw new RangeError("rational.ts: denominator must not be zero");
|
|
20
|
-
const
|
|
21
|
-
const num = n * sign;
|
|
22
|
-
const den = d * sign;
|
|
23
|
-
const g = gcd(num, den);
|
|
20
|
+
const g = gcd(n, d);
|
|
24
21
|
return {
|
|
25
|
-
n:
|
|
26
|
-
d:
|
|
22
|
+
n: n / g,
|
|
23
|
+
d: d / g
|
|
27
24
|
};
|
|
28
25
|
}
|
|
29
26
|
function toExactRational(value) {
|
|
@@ -266,9 +263,7 @@ function negateInterval(a) {
|
|
|
266
263
|
return interval(-a.max, -a.min, a.dimension);
|
|
267
264
|
}
|
|
268
265
|
function absInterval(a) {
|
|
269
|
-
|
|
270
|
-
if (a.max <= 0) return negateInterval(a);
|
|
271
|
-
return interval(0, Math.max(-a.min, a.max), a.dimension);
|
|
266
|
+
return interval(Math.max(0, a.min, -a.max), Math.max(-a.min, a.max), a.dimension);
|
|
272
267
|
}
|
|
273
268
|
//#endregion
|
|
274
269
|
//#region src/compute/evaluate.ts
|
|
@@ -282,7 +277,7 @@ function isInterval(value) {
|
|
|
282
277
|
function toInterval(value) {
|
|
283
278
|
return isInterval(value) ? value : pointInterval(value.magnitude, value.dimension);
|
|
284
279
|
}
|
|
285
|
-
function asQuantity
|
|
280
|
+
function asQuantity(value, context) {
|
|
286
281
|
if (isInterval(value)) throw new UnsupportedExpressionError(context, "this position requires a plain Quantity, not an Interval");
|
|
287
282
|
return value;
|
|
288
283
|
}
|
|
@@ -334,6 +329,9 @@ function evaluate(expression, bindings, context = EMPTY_SYMBOL_TABLE$2) {
|
|
|
334
329
|
case "unparsed": throw new UnsupportedExpressionError("evaluate", `this node is source LaTeX ("${expression.latex}") document-schema.js's lowering could not represent structurally, so there is nothing to evaluate`);
|
|
335
330
|
}
|
|
336
331
|
}
|
|
332
|
+
function evaluateQuantity(expression, bindings, context = EMPTY_SYMBOL_TABLE$2) {
|
|
333
|
+
return asQuantity(evaluate(expression, bindings, context), "evaluateQuantity");
|
|
334
|
+
}
|
|
337
335
|
function evaluateQty(node, context) {
|
|
338
336
|
const unit = context.units.find((entry) => entry.id === node.unit);
|
|
339
337
|
if (unit === void 0) throw new UnknownUnitError(node.unit);
|
|
@@ -370,13 +368,13 @@ function evaluateApp(node, bindings, context) {
|
|
|
370
368
|
}
|
|
371
369
|
if (node.operator === "math:pow") {
|
|
372
370
|
const [base, exponent] = expectTwoArgs(args, "'math:pow'");
|
|
373
|
-
return powQuantity(asQuantity
|
|
371
|
+
return powQuantity(asQuantity(base, "evaluate"), asQuantity(exponent, "evaluate"));
|
|
374
372
|
}
|
|
375
373
|
throw new UnsupportedExpressionError("evaluate", `unknown operator '${node.operator}'`);
|
|
376
374
|
}
|
|
377
375
|
function evaluateBinder(node, bindings, context) {
|
|
378
|
-
const lower = asQuantity
|
|
379
|
-
const upper = asQuantity
|
|
376
|
+
const lower = asQuantity(evaluate(node.lower, bindings, context), `evaluate:${node.kind}`);
|
|
377
|
+
const upper = asQuantity(evaluate(node.upper, bindings, context), `evaluate:${node.kind}`);
|
|
380
378
|
if (!isDimensionless(lower.dimension) || !isDimensionless(upper.dimension)) throw new IncompatibleDimensionsError(`math:${node.kind}`, lower.dimension, upper.dimension, "binder bounds must be dimensionless");
|
|
381
379
|
if (!Number.isInteger(lower.magnitude) || !Number.isInteger(upper.magnitude)) throw new UnsupportedExpressionError(`evaluate:${node.kind}`, "binder bounds must evaluate to integers");
|
|
382
380
|
let accumulator = node.kind === "sum" ? quantity(0, {}) : quantity(1, {});
|
|
@@ -385,7 +383,7 @@ function evaluateBinder(node, bindings, context) {
|
|
|
385
383
|
...bindings,
|
|
386
384
|
[node.binder]: quantity(i, {})
|
|
387
385
|
};
|
|
388
|
-
const bodyValue = asQuantity
|
|
386
|
+
const bodyValue = asQuantity(evaluate(node.body, bodyBindings, context), `evaluate:${node.kind}`);
|
|
389
387
|
accumulator = node.kind === "sum" ? addQuantities(accumulator, bodyValue) : multiplyQuantities(accumulator, bodyValue);
|
|
390
388
|
}
|
|
391
389
|
return accumulator;
|
|
@@ -429,7 +427,7 @@ function bisection(f, bracket, tolerance, maxIterations) {
|
|
|
429
427
|
const mid = (low + high) / 2;
|
|
430
428
|
const fMid = f(mid);
|
|
431
429
|
if (Math.abs(fMid) < tolerance) return mid;
|
|
432
|
-
if (fMid
|
|
430
|
+
if (Math.sign(fMid) === Math.sign(fLow)) {
|
|
433
431
|
low = mid;
|
|
434
432
|
fLow = fMid;
|
|
435
433
|
} else high = mid;
|
|
@@ -444,9 +442,7 @@ function newton(f, initialGuess, tolerance, maxIterations, h) {
|
|
|
444
442
|
if (Math.abs(fx) < tolerance) return x;
|
|
445
443
|
const derivative = (f(x + h) - f(x - h)) / (2 * h);
|
|
446
444
|
if (!Number.isFinite(derivative) || Math.abs(derivative) < 1e-14) throw new NonConvergentSolveError("newton", i, `the numeric derivative vanished or diverged near x=${x}`);
|
|
447
|
-
|
|
448
|
-
if (!Number.isFinite(next)) throw new NonConvergentSolveError("newton", i, `the iteration diverged to a non-finite value near x=${x}`);
|
|
449
|
-
x = next;
|
|
445
|
+
x = x - fx / derivative;
|
|
450
446
|
}
|
|
451
447
|
throw new NonConvergentSolveError("newton", maxIterations, `residual still exceeds tolerance ${tolerance} after ${maxIterations} iterations`);
|
|
452
448
|
}
|
|
@@ -470,19 +466,13 @@ function asEquality(expression) {
|
|
|
470
466
|
function containsSymbol(expression) {
|
|
471
467
|
switch (expression.kind) {
|
|
472
468
|
case "sym": return true;
|
|
473
|
-
case "num":
|
|
474
|
-
case "qty":
|
|
475
|
-
case "unparsed": return false;
|
|
476
469
|
case "app": return expression.args.some(containsSymbol);
|
|
477
470
|
case "sum":
|
|
478
471
|
case "prod": return containsSymbol(expression.lower) || containsSymbol(expression.upper) || containsSymbol(expression.body);
|
|
479
472
|
case "matrix": return expression.rows.some((row) => row.some(containsSymbol));
|
|
473
|
+
default: return false;
|
|
480
474
|
}
|
|
481
475
|
}
|
|
482
|
-
function asQuantity(value) {
|
|
483
|
-
if (isInterval(value)) throw new UnsupportedExpressionError("runWorkedExampleSequence", "this harness compares point-valued Quantity answers only; a symbol resolving to a range (Interval) has no stated-answer comparison defined yet");
|
|
484
|
-
return value;
|
|
485
|
-
}
|
|
486
476
|
function gapFromError(error) {
|
|
487
477
|
if (error instanceof UnboundSymbolError) return "unbound-symbol";
|
|
488
478
|
if (error instanceof UnknownUnitError) return "unknown-unit";
|
|
@@ -490,7 +480,6 @@ function gapFromError(error) {
|
|
|
490
480
|
if (error instanceof DivisionByZeroError) return "division-by-zero";
|
|
491
481
|
if (error instanceof UnsupportedExpressionError) return "unsupported-construct";
|
|
492
482
|
if (error instanceof NumericDomainError) return "numeric-domain";
|
|
493
|
-
if (error instanceof NonConvergentSolveError) return "non-convergent-solve";
|
|
494
483
|
return "other-evaluation-error";
|
|
495
484
|
}
|
|
496
485
|
function errorMessage(error) {
|
|
@@ -532,7 +521,7 @@ function runWorkedExampleSequence(formulas, symbolTable = EMPTY_SYMBOL_TABLE, op
|
|
|
532
521
|
}
|
|
533
522
|
let closedValue;
|
|
534
523
|
try {
|
|
535
|
-
closedValue =
|
|
524
|
+
closedValue = evaluateQuantity(rhs, EMPTY_BINDINGS, symbolTable);
|
|
536
525
|
} catch (error) {
|
|
537
526
|
outcomes.push({
|
|
538
527
|
outcome: "gap",
|
|
@@ -547,7 +536,7 @@ function runWorkedExampleSequence(formulas, symbolTable = EMPTY_SYMBOL_TABLE, op
|
|
|
547
536
|
pending = void 0;
|
|
548
537
|
let actual;
|
|
549
538
|
try {
|
|
550
|
-
actual =
|
|
539
|
+
actual = evaluateQuantity(definitionRhs, bindings, symbolTable);
|
|
551
540
|
} catch (error) {
|
|
552
541
|
outcomes.push({
|
|
553
542
|
outcome: "gap",
|
|
@@ -657,4 +646,4 @@ function formatCorpusReport(report) {
|
|
|
657
646
|
return lines.join("\n");
|
|
658
647
|
}
|
|
659
648
|
//#endregion
|
|
660
|
-
export { DivisionByZeroError, IncompatibleDimensionsError, NonConvergentSolveError, NumericDomainError, UnboundSymbolError, UnknownUnitError, UnsupportedExpressionError, absInterval, absQuantity, addIntervals, addQuantities, addRational, collectFormulas, cosQuantity, dimensionExponent, dimensionToString, dimensionsEqual, divideDimensions, divideIntervals, divideQuantities, divideRational, evaluate, formatCorpusReport, interval, isDimensionless, isInterval, multiplyDimensions, multiplyIntervals, multiplyQuantities, multiplyRational, negateInterval, negateQuantity, pointInterval, powQuantity, quantity, rationalToNumber, runCorpus, runWorkedExampleSequence, scaleDimension, sinQuantity, solveFor, sqrtQuantity, subtractIntervals, subtractQuantities, subtractRational, tanQuantity, toExactRational, toRational };
|
|
649
|
+
export { DivisionByZeroError, IncompatibleDimensionsError, NonConvergentSolveError, NumericDomainError, UnboundSymbolError, UnknownUnitError, UnsupportedExpressionError, absInterval, absQuantity, addIntervals, addQuantities, addRational, collectFormulas, cosQuantity, dimensionExponent, dimensionToString, dimensionsEqual, divideDimensions, divideIntervals, divideQuantities, divideRational, evaluate, evaluateQuantity, formatCorpusReport, interval, isDimensionless, isInterval, multiplyDimensions, multiplyIntervals, multiplyQuantities, multiplyRational, negateInterval, negateQuantity, pointInterval, powQuantity, quantity, rationalToNumber, runCorpus, runWorkedExampleSequence, scaleDimension, sinQuantity, solveFor, sqrtQuantity, subtractIntervals, subtractQuantities, subtractRational, tanQuantity, toExactRational, toRational };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "document-compute.js",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.9",
|
|
4
4
|
"description": "Units-typed, tree-walking evaluator for document-schema.js's MathExpression -- exact-rational unit conversion, interval arithmetic, and bisection/Newton numeric solve-for, the compute package for the documents.js family.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"@stryker-mutator/typescript-checker": "10.0.0",
|
|
80
80
|
"@stryker-mutator/vitest-runner": "10.0.0",
|
|
81
81
|
"@types/node": "26.2.0",
|
|
82
|
-
"documents.js": "7.20.
|
|
82
|
+
"documents.js": "7.20.6",
|
|
83
83
|
"eslint": "10.8.1",
|
|
84
84
|
"husky": "9.1.7",
|
|
85
85
|
"markdown-codec": "6.7.1",
|