Skip to content

Commit dc6e098

Browse files
committed
Add stable scientific scalar substrate
1 parent 6185ed1 commit dc6e098

17 files changed

Lines changed: 5317 additions & 878 deletions

README.md

Lines changed: 113 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -320,19 +320,28 @@ assert_eq!(value.exact_rational(), Some(Rational::fraction(7, 8).unwrap()));
320320
```
321321

322322
`Simple` supports arithmetic, roots, powers, logs, exponentials, stable scalar
323-
helpers (`ln_1p`/`log1p`, `ln_1m`/`log1m`, `expm1`, `softplus`, `logit`,
324-
`sigmoid`), trig and pi-scaled trig (`sin_pi`, `cos_pi`, `tan_pi`), small-angle
325-
helpers (`sinc`, `sinc_pi`, `cosc`), inverse trig, inverse hyperbolic functions,
326-
normal distribution helpers (`erf`, `erfc`, `erfcx`, `dnorm`, `pnorm`,
327-
`normal_sf`, `pnorm_upper`, `normal_interval`, `pnorm_diff`, `log_pnorm`,
328-
`log_normal_sf`, `log_dnorm`, `erfinv`, `erfcinv`, `qnorm`, `qnorm_upper`,
329-
`normal_pdf`, `normal_cdf`, `normal_survival`, `normal_quantile`,
330-
`normal_mills`, `normal_hazard`, `normal_log_hazard`, `normal_inverse_mills`,
331-
`hermite_probabilists`, `dnorm_derivative`, `gaussian_derivative`,
332-
`standard_normal_moment`, `normal_interval_moment`, `truncated_normal_mean`,
333-
`truncated_normal_variance`, `regularized_gamma_p`, `regularized_gamma_q`,
334-
`chi_square_cdf`, `chi_square_sf`), integers, decimals, fractions, `pi`, and
335-
`e`.
323+
helpers (`ln_1p`/`log1p`, `ln_1m`/`log1m`, `expm1`, `softplus`,
324+
`logaddexp`, `logsubexp`, `logit`, `sigmoid`), trig and pi-scaled trig
325+
(`sin_pi`, `cos_pi`, `tan_pi`), small-angle helpers (`sinc`, `sinc_pi`,
326+
`cosc`), cancellation helpers (`sqrt1pm1`, `sqrt1m1`, `hypot_minus`),
327+
product-sum helpers (`mul_add`, `sum_products`, `diff_of_products`),
328+
polynomial helpers (`eval_poly`, `eval_rational_poly`), vector length helpers
329+
(`hypot2`, `hypot3`),
330+
exact-root/rational-power helpers (`cbrt`, `root_n`, `pow_rational`),
331+
certified integer helpers (`floor_certified`,
332+
`ceil_certified`, `round_certified`, `trunc_certified`, `fract_certified`,
333+
`rem_euclid_certified`), inverse trig, inverse hyperbolic functions, normal
334+
distribution helpers (`erf`, `erfc`, `erfcx`, `dnorm`,
335+
`pnorm`, `normal_sf`, `pnorm_upper`, `normal_interval`, `pnorm_diff`,
336+
`log_pnorm`, `log_normal_sf`, `log_dnorm`, `erfinv`, `erfcinv`, `qnorm`,
337+
`qnorm_upper`, `normal_pdf`, `normal_cdf`, `normal_survival`,
338+
`normal_quantile`, `normal_mills`, `normal_hazard`, `normal_log_hazard`,
339+
`normal_inverse_mills`, `hermite_probabilists`, `dnorm_derivative`,
340+
`gaussian_derivative`, `standard_normal_moment`, `normal_interval_moment`,
341+
`truncated_normal_mean`, `truncated_normal_variance`, `gamma`, `lgamma`,
342+
`beta`, `ln_beta`/`lbeta`, `regularized_beta`, `regularized_beta_q`,
343+
`regularized_gamma_p`, `regularized_gamma_q`, `chi_square_cdf`,
344+
`chi_square_sf`), integers, decimals, fractions, `pi`, and `e`.
336345

337346
Stability-oriented scalar forms keep common statistical expressions from being
338347
assembled out of cancellation-prone generic arithmetic:
@@ -343,6 +352,8 @@ assembled out of cancellation-prone generic arithmetic:
343352
| `(ln_1m x)` / `(log1m x)` | natural log of `1 - x` | `x < 1` |
344353
| `(expm1 x)` | `exp(x) - 1` with a small-argument kernel | all real inputs |
345354
| `(softplus x)` | `ln(1 + exp(x))` | all real inputs |
355+
| `(logaddexp a b)` | `ln(exp(a) + exp(b))` | all real inputs |
356+
| `(logsubexp a b)` | `ln(exp(a) - exp(b))` | certifiable `a > b` |
346357
| `(logit p)` | `ln(p / (1 - p))` | `0 < p < 1` |
347358
| `(sigmoid x)` | `1 / (1 + exp(-x))` | all real inputs |
348359

@@ -364,6 +375,65 @@ division-by-zero at the origin:
364375
| `(sinc_pi x)` | `sin(pi * x) / (pi * x)`, with `sinc_pi(0) = 1` | all real inputs |
365376
| `(cosc x)` | `(1 - cos(x)) / x^2`, with `cosc(0) = 1/2` | all real inputs |
366377

378+
Cancellation helper forms preserve common square-root differences instead of
379+
forcing users to spell them as near-equal subtraction:
380+
381+
| Form | Meaning | Domain |
382+
| --- | --- | --- |
383+
| `(sqrt1pm1 x)` | `sqrt(1 + x) - 1` | `x >= -1` |
384+
| `(sqrt1m1 x)` | `sqrt(1 - x) - 1` | `x <= 1` |
385+
| `(hypot_minus x y)` | `sqrt(x^2 + y^2) - x` | all real inputs |
386+
387+
Product-sum forms preserve common fused arithmetic shapes before expanding into
388+
generic arithmetic. `sum_products` takes flat product pairs:
389+
390+
| Form | Meaning | Domain |
391+
| --- | --- | --- |
392+
| `(mul_add a b c)` | `a*b + c` | all real inputs |
393+
| `(sum_products x0 y0 x1 y1 ...)` | `x0*y0 + x1*y1 + ...` | even number of operands |
394+
| `(diff_of_products a b c d)` | `a*b - c*d` | all real inputs |
395+
396+
Polynomial forms preserve Horner and rational-polynomial evaluation structure.
397+
Simple has flat operands, so rational polynomials use a numerator coefficient
398+
count after `x`; polynomial coefficients are constant-first. Bernstein and
399+
de Casteljau operations carry curve-basis semantics and belong in curve-level
400+
crates such as `hypercurve`.
401+
402+
| Form | Meaning | Domain |
403+
| --- | --- | --- |
404+
| `(eval_poly x c0 c1 c2 ...)` | `c0 + c1*x + c2*x^2 + ...` in Horner form | at least one coefficient |
405+
| `(eval_rational_poly x n c0 ... d0 ...)` | numerator polynomial divided by denominator polynomial; `n` is numerator coefficient count | exact non-negative integer `n`, at least one denominator coefficient, non-zero denominator value |
406+
407+
Scalar vector-length forms route through the exact dot-product reducers before
408+
taking square roots, so rational Pythagorean cases can stay exact:
409+
410+
| Form | Meaning | Domain |
411+
| --- | --- | --- |
412+
| `(hypot2 x y)` | `sqrt(x^2 + y^2)` | all real inputs |
413+
| `(hypot3 x y z)` | `sqrt(x^2 + y^2 + z^2)` | all real inputs |
414+
415+
Root and exact rational-power forms preserve perfect rational roots before
416+
falling back to exact-real rational exponents:
417+
418+
| Form | Meaning | Domain |
419+
| --- | --- | --- |
420+
| `(cbrt x)` | cube root of `x` | all real inputs |
421+
| `(root_n x n)` | nth root of `x` | exact positive integer `n`; negative `x` requires odd `n` |
422+
| `(pow_rational x q)` | `x^q` with exact rational exponent `q` | all positive `x`; negative `x` requires an odd denominator |
423+
424+
Certified integer forms make discontinuous decisions through exact rational
425+
shortcuts or bounded exact-real comparison. If a boundary cannot be certified,
426+
they return `Problem::Exhausted` rather than rounding through a primitive float:
427+
428+
| Form | Meaning | Domain |
429+
| --- | --- | --- |
430+
| `(floor_certified x)` | greatest integer `<= x` | certifiable integer boundary |
431+
| `(ceil_certified x)` | least integer `>= x` | certifiable integer boundary |
432+
| `(round_certified x)` | nearest integer, ties away from zero | certifiable half-integer boundary |
433+
| `(trunc_certified x)` | integer part toward zero | certifiable sign and integer boundary |
434+
| `(fract_certified x)` | `x - trunc_certified(x)` | certifiable truncation |
435+
| `(rem_euclid_certified x m)` | Euclidean remainder for positive modulus | certifiable `m > 0` and quotient floor |
436+
367437
Normal-distribution forms use the same `Real` methods as Rust callers:
368438

369439
| Form | Meaning | Domain |
@@ -399,15 +469,25 @@ Normal-distribution forms use the same `Real` methods as Rust callers:
399469
| `(normal_interval_moment lo hi n)` | unnormalized raw moment over `[lo, hi]` | exact non-negative integer `n`, finite bounds with `abs(bound) <= 10` and `lo <= hi` |
400470
| `(truncated_normal_mean lo hi)` | mean of a standard normal truncated to `[lo, hi]` | finite bounds with `abs(bound) <= 10` and `lo < hi` |
401471
| `(truncated_normal_variance lo hi)` | variance of a standard normal truncated to `[lo, hi]` | finite bounds with `abs(bound) <= 10` and `lo < hi` |
472+
| `(gamma x)` | gamma function `Gamma(x)` | exact integer or half-integer `x`, excluding non-positive integer poles |
473+
| `(lgamma x)` | natural log of `abs(Gamma(x))` | exact integer or half-integer `x`, excluding non-positive integer poles |
474+
| `(beta a b)` | beta function `B(a, b)` through gamma closed forms | exact integer or half-integer arguments whose gamma ratio is defined |
475+
| `(ln_beta a b)` / `(lbeta a b)` | natural log of `abs(B(a, b))` | exact integer or half-integer arguments whose gamma ratio is defined |
476+
| `(regularized_beta a b x)` | regularized incomplete beta `I_x(a, b)` | exact positive integer `a` and `b`, `0 <= x <= 1` |
477+
| `(regularized_beta_q a b x)` | complement `1 - I_x(a, b)` | exact positive integer `a` and `b`, `0 <= x <= 1` |
402478
| `(regularized_gamma_p a x)` | regularized lower incomplete gamma `P(a, x)` | exact positive integer or half-integer `a`, `x >= 0` |
403479
| `(regularized_gamma_q a x)` | regularized upper incomplete gamma `Q(a, x)` | exact positive integer or half-integer `a`, `x >= 0` |
404480
| `(chi_square_cdf x k)` | chi-square CDF with `k` degrees of freedom | `x >= 0`, exact positive integer `k` |
405481
| `(chi_square_sf x k)` | chi-square upper-tail probability | `x >= 0`, exact positive integer `k` |
406482

407483
Inputs outside those supported numeric ranges return `Problem` rather than silently
408484
falling back to primitive floating point.
409-
`ln_1p`/`log1p`, `ln_1m`/`log1m`, `logit`, and `tan_pi` return
485+
`ln_1p`/`log1p`, `ln_1m`/`log1m`, `logsubexp`, `logit`, and `tan_pi` return
410486
`Problem::NotANumber` outside their open domains.
487+
`sqrt1pm1` and `sqrt1m1` return `Problem::SqrtNegative` when their radicand is
488+
known negative.
489+
`root_n` rejects degree zero and even roots of negative values; `pow_rational`
490+
inherits the existing negative-base rational exponent policy.
411491
Reversed normal-interval bounds return `Problem::NotANumber`; equal bounds return exact
412492
zero.
413493
Parametric normal forms standardize exactly as `(x - mean) / sigma`, or
@@ -421,9 +501,9 @@ Regularized gamma supports the integer and half-integer cases that reduce to
421501
finite recurrences over `erf`/`erfc`, `exp(-x)`, `sqrt(x)`, and exact factorial
422502
coefficients; chi-square helpers are thin wrappers through `P(k/2, x/2)` and
423503
`Q(k/2, x/2)`.
424-
`softplus`, `logit`, and `sigmoid` use sign-stable forms so callers do not need to
425-
spell them through `ln(1 + exp(x))`, `ln(p) - ln(1 - p)`, or
426-
`1 / (1 + exp(-x))`.
504+
`softplus`, `logaddexp`, `logsubexp`, `logit`, and `sigmoid` use sign-stable
505+
forms so callers do not need to spell them through `ln(1 + exp(x))`,
506+
`ln(exp(a) +/- exp(b))`, `ln(p) - ln(1 - p)`, or `1 / (1 + exp(-x))`.
427507

428508
## Conversions
429509

@@ -453,6 +533,7 @@ cargo bench --bench float_convert
453533
cargo bench --bench scalar_micro
454534
cargo bench --bench library_perf --features simple
455535
cargo bench --bench adversarial_transcendentals
536+
cargo bench --bench adversarial_library
456537
```
457538

458539
Run dispatch tracing separately:
@@ -466,6 +547,21 @@ anchors and regression goals for `Rational`, `Real`, and `Computable` are in
466547
[`PERFORMANCE.md`](./PERFORMANCE.md). Dispatch summaries are written to
467548
[`dispatch_trace.md`](./dispatch_trace.md) when tracing is enabled.
468549

550+
For a full post-change performance pass, run:
551+
552+
```sh
553+
cargo bench --all-features
554+
```
555+
556+
The `library_perf` benchmark now has focused groups for the newer stable scalar
557+
substrate, geometry/polynomial helpers, Gaussian/scientific helpers, and `Simple`
558+
parser exposure. The adversarial library run includes tiny residuals, near-domain
559+
boundaries, root and rational-power cases, normal tails, finite gamma/beta
560+
recurrences, and polynomial/vector helper shapes. Dispatch tracing has matching
561+
rows (`real/stable_scalar_substrate`, `real/geometry_polynomial_substrate`, and
562+
`real/normal_scientific_substrate`) so benchmark findings can be connected back
563+
to the representation branch that was taken.
564+
469565
When adding a shortcut, add a focused correctness test and a benchmark row for the
470566
smallest affected surface. Keep the shortcut only if it improves the target without
471567
regressing broader stack-facing paths.

0 commit comments

Comments
 (0)