From feafc81fe4e9dec19b69a98070fe3d9910919ad4 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Thu, 1 Oct 2026 03:40:37 +0000 Subject: [PATCH] A Gaussian below the bar is read as its negated exponent above it e^(-x^2)/x^2 was integrated by the Gaussian's moments, and 1/(e^(x^2) x^2), the same function, was not: the moment rule took the exponential from above the bar only. By parts writes the factor beside erf(b x) in erf(b x)/(e^(b^2 x^2) x^2) the second way, and that integral was declined for want of it. A Gaussian below the bar is read as the exponential of its negated exponent now. Measured on the Rubi corpus, master at 2db42780 and this change on it, run side by side: family 8 367 -> 371 of 420, erf and erfc of b x over e^(b^2 x^2) x^2 and over e^(b^2 x^2) x^4; family 2 544 of 650, the independent suites 1756 of 1814 and families 1 and 3 to 7 at five a file 799 of 897, on both. 0 wrong everywhere. Run alone, the four are 0 of 4 on master and 4 of 4 here, each answered in 0.2 to 0.9 s where master declined it in 2.4 to 10.5 s. The unit tests pass, 14,249. The performance gate passes on c6024927, which is this change on 2db42780: allocation is what the baseline says on all 19 gated benchmarks. GaussianIntegralTest has two rows with the Gaussian below the bar, each differentiated back. Part of #1501. Co-Authored-By: Claude Opus 5.5 --- BREAKING-CHANGES.md | 14 ++++++++++++++ .../Continuous/Integration/IntegralPatterns.cs | 8 ++++++-- .../UnitTests/Calculus/GaussianIntegralTest.cs | 2 ++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 0ab69148c..430aa694f 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -2917,6 +2917,20 @@ since they are these exponentials | `"(p + q*x)^2*f^(a + b*x + c*x^2)".Integrate("x")` | `integral((p + q * x) ^ 2 * f ^ (a + b * x + c * x ^ 2), x)` | the antiderivative | | `"x^2*sinh(a + b*x + c*x^2)".Integrate("x")` | `integral(x ^ 2 * (e ^ (a + b * x + c * x ^ 2) - e ^ (-(a + b * x + c * x ^ 2))) / 2, x)` | the antiderivative | +### A Gaussian below the bar is read as the exponential of its negated exponent + +`e^(-x^2)/x^2` was integrated by the Gaussian's moments, and `1/(e^(x^2) x^2)`, the same function, +was not: the rule for the moments took the exponential from above the bar only. By parts writes +the factor beside `erf(b x)` in `erf(b x)/(e^(b^2 x^2) x^2)` the second way, and that integral was +declined for want of it ([#1501](https://github.com/asc-community/AngouriMath/issues/1501)). Both +columns measured on a build, `v2.5.0` against this change; `erf` had no reading in 2.5.0, which its +own entry records. + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"1/(e^(x^2)*x^2)".Integrate("x")` | `integral(1 / (e ^ x ^ 2 * x ^ 2), x)` | `-1 / x * e ^ (-x ^ 2) + (-2) * pi ^ (1/2) / 2 * erf(x) + C` | +| `"erf(b*x)/(e^(b^2*x^2)*x^2)".Integrate("x")` | `UnrecognizedFunctionParseException`: there is no function `erf` | an antiderivative in `erf` and `Ei(-2 b^2 x^2)` | + ### An exponential of a polynomial beside the polynomial's derivative is integrated `e^(a + b x + c x^2) (b + 2 c x) sqrt(a + b x + c x^2)` was left unintegrated. `G^P k P' f(P)`, with diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IntegralPatterns.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IntegralPatterns.cs index cf2e5a968..b4424296e 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IntegralPatterns.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IntegralPatterns.cs @@ -232,9 +232,13 @@ private static bool TakeMomentFactor(Entity factor, bool inverted, Entity.Variab power = inverted ? -n.EInteger.ToInt32Checked() : n.EInteger.ToInt32Checked(); return true; } - if (!inverted && gaussian is null && factor is Entity.Powf(var @base, _) exponential && !@base.ContainsNode(x)) + // Below the bar too, as the exponential of the negated exponent: `1/(e^(b^2 x^2) x^2)` is + // `e^(-b^2 x^2)/x^2`, and it is how by parts writes the factor beside `erf(b x)` in + // `erf(b x)/(e^(b^2 x^2) x^2)`, which was declined for want of its integral. + // https://github.com/asc-community/AngouriMath/issues/1501 + if (gaussian is null && factor is Entity.Powf(var @base, var exponent) exponential && !@base.ContainsNode(x)) { - gaussian = exponential; + gaussian = inverted ? new Entity.Powf(@base, -exponent) : exponential; return true; } // A sum, or a whole power of one, taken as it is: read as a polynomial only once a diff --git a/Sources/Tests/UnitTests/Calculus/GaussianIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/GaussianIntegralTest.cs index ff5dac019..3cfe8110f 100644 --- a/Sources/Tests/UnitTests/Calculus/GaussianIntegralTest.cs +++ b/Sources/Tests/UnitTests/Calculus/GaussianIntegralTest.cs @@ -103,6 +103,8 @@ public void ASymbolicGaussianIsUnconditional() [InlineData("e^(-x^2)/x^2")] [InlineData("e^(x^2)/x^4")] [InlineData("3*x^6*e^(-2*x^2)/5")] + [InlineData("1/(e^(x^2)*x^2)")] + [InlineData("1/(e^(2*x^2)*x^4)")] public void TheGaussianBesideAnEvenPower(string integrand) => DifferentiatesBack(integrand); ///