diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index c78613901..4772039dc 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -1710,6 +1710,18 @@ 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 | +### 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 +`P` a polynomial of degree two or more and `P'` a factor of its own up to a constant, is now +integrated under `u = P`, as `k G^u f(u)` +([#1501](https://github.com/asc-community/AngouriMath/issues/1501)). + +| Input | Was (2.5.0) | Now | +|---|---|---| +| `"e^(a + b*x + c*x^2)*(b + 2*c*x)*(a + b*x + c*x^2)^(1/2)".Integrate("x")` | `integral(e ^ (a + b * x + c * x ^ 2) * (b + 2 * c * x) * sqrt(a + b * x + c * x ^ 2), x)` | the antiderivative, with `erfi` | +| `"e^(a + b*x + c*x^2)*(b + 2*c*x)/(a + b*x + c*x^2)^(3/2)".Integrate("x")` | `integral(e ^ (a + b * x + c * x ^ 2) * (b + 2 * c * x) / (a + b * x + c * x ^ 2) ^ (3/2), x)` | the antiderivative, with `erfi` | + ### An exponential of a quadratic in `1/x` or `1/(c + d x)` is integrated `e^(-1/x^2)` was left unintegrated. `L^m F^(A/L^2 + B/L + C)`, with `L = c + d x` and a whole `m`, diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 6a3e13657..b79c1e89f 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -17193,6 +17193,63 @@ private static bool IsARationalFunctionOfExponentials(Entity expr, Entity.Variab } } + /// + /// An exponential of a polynomial beside the polynomial's derivative, with the rest a + /// function of the polynomial: G^P k P' f(P) is k G^u f(u) under u = P. + /// Rubi's 2.3, e^(a + b x + c x^2) (b + 2 c x) (a + b x + c x^2)^(n/2). + /// https://github.com/asc-community/AngouriMath/issues/1501 + /// + /// + /// reads this u and does not reach it: for its + /// own search it writes an exponential of a sum as a product of exponentials, so that + /// e^(e^x) e^x is e^u du, and e^(a + b x + c x^2) is then + /// e^a e^(b x) e^(c x^2), with no P left in it to replace. Declining took + /// it 24 s. Only a polynomial of degree two or more, whose derivative is a factor of its + /// own: an exponential of a linear is the table's. + /// + internal static Entity? SolveByTheExponentAsTheVariable(Entity expr, Entity.Variable x, bool integrateByParts) + { + if (expr is not (Mulf or Divf) || AnExponentialOfAPolynomial(expr, x) is not { } exponential) + return null; + var polynomial = exponential.Exponent; + var derivative = polynomial.Differentiate(x); + Entity? scale = null; + Entity rest = Number.Integer.One; + foreach (var (factor, underneath) in FactorsOfTheIntegrand(expr)) + { + // The derivative, up to a constant, above the bar: a sum, as it is written. + if (scale is null && !underneath && factor is Sumf or Minusf && factor.ContainsNode(x) + && Functions.PartialFractions.Bare((factor / derivative).Simplify()) is var ratio && !ratio.ContainsNode(x) + && ratio.Evaled is not Number.Complex { IsZero: true }) + { + scale = ratio; + continue; + } + rest = underneath ? rest / factor : rest * factor; + } + if (scale is null) + return null; + var u = Variable.CreateUnique(expr, "u"); + var inU = (scale * rest.Replace(node => node == polynomial ? u : node)).InnerSimplified; + if (inU.ContainsNode(x)) + return null; + return Integration.ComputeAsAQuestionOfItsOwn(inU, u, integrateByParts)?.Substitute(u, polynomial); + } + + /// + /// A factor of , read through products and quotients, that is a + /// constant to a polynomial in of degree two or more, or . + /// + private static Powf? AnExponentialOfAPolynomial(Entity expr, Entity.Variable x) => expr switch + { + Mulf(var left, var right) => AnExponentialOfAPolynomial(left, x) ?? AnExponentialOfAPolynomial(right, x), + Divf(var numerator, var denominator) => AnExponentialOfAPolynomial(numerator, x) ?? AnExponentialOfAPolynomial(denominator, x), + Powf(var @base, Sumf or Minusf) power when !@base.ContainsNode(x) && power.Exponent.ContainsNode(x) + && TreeAnalyzer.TryGetPolynomial(power.Exponent, x, out var monomials) + && monomials.Keys.All(degree => degree.Sign >= 0) && monomials.Keys.Any(degree => degree.CompareTo(EInteger.One) > 0) => power, + _ => null + }; + /// /// Attempts to solve an integral using u-substitution. /// Looks for patterns where f(g(x)) * g'(x) can be integrated as F(g(x)). diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs index abcd8827b..a72bfc477 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs @@ -579,6 +579,9 @@ private static Entity Normalized(Entity expr, Entity.Variable x) => // An exponential of a multiple of a logarithm is a power of the argument, which is // how every inverse hyperbolic function under an exponential arrives. if ((answer = IndefiniteIntegralSolver.SolveByFoldingAnExponentialOfALogarithm(expr, x, integrateByParts)) is { }) return answer; + // An exponential of a polynomial beside the polynomial's derivative, under u = P, + // which the substitution search does not reach: it writes the exponential apart. + if ((answer = IndefiniteIntegralSolver.SolveByTheExponentAsTheVariable(expr, x, integrateByParts)) is { }) return answer; // `A + i A tan(z)` is `A e^(i z)/cos(z)`, which beside a polynomial is a shape the // closed rules answer, where the imaginary unit in the coefficient is read by none. if ((answer = IndefiniteIntegralSolver.SolveByWritingAnImaginaryTangentAsAnExponential(expr, x, integrateByParts)) is { }) return answer; diff --git a/Sources/Tests/UnitTests/Calculus/GaussianIntegralTest.cs b/Sources/Tests/UnitTests/Calculus/GaussianIntegralTest.cs index 2ecd98370..52ba97dd1 100644 --- a/Sources/Tests/UnitTests/Calculus/GaussianIntegralTest.cs +++ b/Sources/Tests/UnitTests/Calculus/GaussianIntegralTest.cs @@ -146,6 +146,19 @@ public void ASymbolicGaussianWithALinearTermBesideAPolynomial(string integrand) public void ASymbolicGaussianInAReciprocal(string integrand) => DifferentiatesBack(integrand, ("f", "2"), ("a", "1/3"), ("b", "-2/3"), ("c", "5"), ("d", "3/2")); + /// + /// An exponential of a quadratic beside the quadratic's derivative and a half-odd power of + /// the quadratic, under u = a + b x + c x^2: e^u u^(n/2). Rubi's 2.3. The + /// quadratic is positive at every point for these pins. + /// + [Theory] + [InlineData("e^(a + b*x + c*x^2)*(b + 2*c*x)*(a + b*x + c*x^2)^(1/2)")] + [InlineData("e^(a + b*x + c*x^2)*(b + 2*c*x)*(a + b*x + c*x^2)^(5/2)")] + [InlineData("e^(a + b*x + c*x^2)*(b + 2*c*x)/(a + b*x + c*x^2)^(3/2)")] + [InlineData("3*e^(a + b*x + c*x^2)*(2*b + 4*c*x)/sqrt(a + b*x + c*x^2)")] + public void TheExponentAsTheVariable(string integrand) + => DifferentiatesBack(integrand, ("a", "1/3"), ("b", "-2/3"), ("c", "5/4")); + /// /// An odd negative power ends at int e^(A x^2)/x, which is the exponential /// integral, so it is not taken.