Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,21 @@ too. A correct antiderivative in an unhelpful form, where there was none at all.
`e^(x^2)` is still declined, and correctly: its exponent is not linear and it has no elementary
antiderivative.

### An exponential of the reciprocal of a linear beside a power of it is integrated

**Answers where there were none.** `F^(a + b/(c + d x)) (c + d x)^2` was declined, and so were
`F^(a + b/(c + d x))` alone, over `c + d x`, and with the cube of the reciprocal in the exponent:
under `u = 1/(c + d x)` each is an exponential beside a power of `u`, and the substitution was made
only where the exponent is a Gaussian in `u`, whose moments the table answers. It is made for any
polynomial exponent in `u` now, and the question asked in `u`. Rubi's 2.3
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"F^(a + b/(c + d*x))*(c + d*x)^2".ToEntity().Integrate("x")` | `integral(...)` | an exponential integral and powers of `c + d x` times the exponential |
| `"F^(a + b/(c + d*x))/(c + d*x)".ToEntity().Integrate("x")` | `integral(...)` | an exponential integral |
| `"F^(a + b/(c + d*x)^3)*(c + d*x)^2".ToEntity().Integrate("x")` | `integral(...)` | an exponential integral of the cube, and the exponential |

### A polynomial over a power of a binomial past the cube is integrated

**Answers where there were none.** `P(x)/(a + b x^n)^k` with symbols in the binomial, `n >= 3`, was
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15993,6 +15993,63 @@ private static bool TryReadAPowerTimesARadicalQuadratic(Entity expr, Entity.Vari
/// that <c>1/(1/u)^2</c> is <c>u^2</c>.
/// </remarks>
internal static Entity? SolveAGaussianInAReciprocal(Entity expr, Entity.Variable x)
{
if (ReadAnExponentialInTheReciprocalOfALinear(expr, x) is not var (constant, @base, exponentInU, u, linear, slope, power)
|| !TreeAnalyzer.TryGetPolyQuadratic(exponentInU, u, out var square, out _, out _) || TreeAnalyzer.IsZero(square))
return null;
var powerInU = -power - EInteger.FromInt32(2);
if (!powerInU.CanFitInInt32() || powerInU.Abs().CompareTo(EInteger.FromInt32(32)) > 0)
return null;
var gaussian = MathS.Pow(@base, exponentInU);
var inU = powerInU.IsZero ? gaussian : MathS.Pow(u, Number.Integer.Create(powerInU)) * gaussian;
if (IntegralPatterns.TryStandardIntegrals(inU, u) is not { } answerInU)
return null;
return (-constant / slope * answerInU.Substitute(u, 1 / linear)).InnerSimplified;
}

/// <summary>
/// The same substitution where the exponent is not a Gaussian in <c>u = 1/L</c>:
/// <c>L^m F^(a + b/L)</c> or <c>L^m F^(a + b/L^3)</c> is
/// <c>-(1/d) u^(-m - 2) F^(a + b u)</c> or <c>F^(a + b u^3)</c>, an exponential beside a
/// power, asked as a question in <c>u</c> and written back with <c>u = 1/L</c>.
/// </summary>
/// <remarks>
/// Rubi's 2.3, <c>F^(a + b/(c + d x)) (c + d x)^2</c>, <c>F^(a + b/(c + d x))/(c + d x)</c>
/// and <c>F^(a + b/(c + d x)^3)/(c + d x)</c>, were declined: the substitution was made only
/// for a Gaussian, whose moments the table answers, and an exponential of a linear or a
/// cube in <c>u</c> beside a power of it is the exponential integral's, or a power of the
/// cube's argument under <c>w = u^3</c>, which the integrator answers when asked in
/// <c>u</c>.
/// https://github.com/asc-community/AngouriMath/issues/718
/// </remarks>
internal static Entity? SolveAnExponentialInTheReciprocalOfALinear(Entity expr, Entity.Variable x, bool integrateByParts)
{
if (ReadAnExponentialInTheReciprocalOfALinear(expr, x) is not var (constant, @base, exponentInU, u, linear, slope, power)
|| !TreeAnalyzer.TryGetPolynomial(exponentInU, u, out var monomials)
|| monomials.Keys.Any(k => k.Sign < 0 || k.CompareTo(EInteger.FromInt32(8)) > 0)
|| !monomials.Keys.Any(k => k.Sign > 0))
return null;
var powerInU = -power - EInteger.FromInt32(2);
if (!powerInU.CanFitInInt32() || powerInU.Abs().CompareTo(EInteger.FromInt32(32)) > 0)
return null;
var exponential = MathS.Pow(@base, exponentInU);
var inU = powerInU.IsZero ? exponential : MathS.Pow(u, Number.Integer.Create(powerInU)) * exponential;
if (Integration.ComputeAsAQuestionOfItsOwn(inU, u, integrateByParts) is not { } answerInU
|| answerInU.Nodes.Any(node => node == MathS.NaN))
return null;
return (-constant / slope * answerInU.Substitute(u, 1 / linear)).InnerSimplified;
}

/// <summary>
/// <paramref name="expr"/> read as a constant times <c>F^E</c> times a whole power of a
/// linear <c>L</c>, with every <c>x</c> in <c>E</c> inside a reciprocal power of <c>L</c>:
/// the constant, <c>F</c>, <c>E</c> written in <c>u = 1/L</c>, <c>u</c>, <c>L</c>, its slope
/// and the power of <c>L</c>. The exponent is rewritten into <c>u</c> a node at a time --
/// <c>b/L^k</c> is <c>b u^k</c> -- rather than by substituting <c>x = (1/u - c)/d</c> and
/// asking the simplifier to see that <c>1/(1/u)^2</c> is <c>u^2</c>.
/// </summary>
private static (Entity Constant, Entity Base, Entity ExponentInU, Variable U, Entity Linear, Entity Slope, EInteger Power)?
ReadAnExponentialInTheReciprocalOfALinear(Entity expr, Entity.Variable x)
{
Powf? exponential = null;
Entity? linear = null;
Expand Down Expand Up @@ -16031,16 +16088,9 @@ private static bool TryReadAPowerTimesARadicalQuadratic(Entity expr, Entity.Vari
Powf(var below, Number.Integer { EInteger.Sign: < 0 } k) when below == linear => MathS.Pow(u, -k),
_ => node
});
if (exponentInU.ContainsNode(x) || !TreeAnalyzer.TryGetPolyQuadratic(exponentInU, u, out var square, out _, out _) || TreeAnalyzer.IsZero(square))
return null;
var powerInU = -power - EInteger.FromInt32(2);
if (!powerInU.CanFitInInt32() || powerInU.Abs().CompareTo(EInteger.FromInt32(32)) > 0)
if (exponentInU.ContainsNode(x))
return null;
var gaussian = MathS.Pow(exponential.Base, exponentInU);
var inU = powerInU.IsZero ? gaussian : MathS.Pow(u, Number.Integer.Create(powerInU)) * gaussian;
if (IntegralPatterns.TryStandardIntegrals(inU, u) is not { } answerInU)
return null;
return (-constant / slope * answerInU.Substitute(u, 1 / linear)).InnerSimplified;
return (constant, exponential.Base, exponentInU, u, linear, slope, power);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,10 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
// an ansatz finds it or nothing does. After the substitutions, which answer the
// linear-exponent cases in their own terms.
if ((answer = IndefiniteIntegralSolver.SolveByExponentialAnsatz(expr, x)) is { }) return answer;
// An exponential of a polynomial in the reciprocal of a linear that is not a Gaussian,
// under the Gaussian's substitution `u = 1/L`. After the ansatz, which answers some of
// these in the base: `f^(a + b/x)/x^4` is `f^(a + b/x)` times a polynomial in `1/x`.
if ((answer = IndefiniteIntegralSolver.SolveAnExponentialInTheReciprocalOfALinear(expr, x, integrateByParts)) is { }) return answer;
// A polynomial times a fractional power of a base that holds a function of x,
// answered as a polynomial times the next power of the base: a linear system in
// the polynomial's coefficients, exact, and volunteered at any depth.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// Copyright (c) 2019-2026 Angouri.
// AngouriMath is licensed under MIT.
// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
// Website: https://am.angouri.org.
//

using System;
using AngouriMath.Extensions;
using Xunit;

namespace AngouriMath.Tests.Calculus
{
/// <summary>
/// An exponential of a power of the reciprocal of a linear, beside a power of the linear, that
/// is not a Gaussian in <c>u = 1/L</c>: <c>F^(a + b/L) L^m</c> is
/// <c>-(1/d) u^(-m - 2) F^(a + b u)</c>, an exponential beside a power. Rubi's 2.3.
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class ExponentialOfTheReciprocalIntegralTest
{
[Theory]
[InlineData("F^(a + b/(c + d*x))*(c + d*x)^2")]
[InlineData("F^(a + b/(c + d*x))")]
[InlineData("F^(a + b/(c + d*x))/(c + d*x)")]
[InlineData("F^(a + b/(c + d*x)^3)/(c + d*x)")]
[InlineData("F^(a + b/(c + d*x)^3)*(c + d*x)^2")]
public void UnderTheReciprocal(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
Assert.DoesNotContain("integral(", integral.Stringize());
Entity Pinned(Entity e) => e.Substitute("F", 2.3).Substitute("a", 0.4).Substitute("b", 1.1)
.Substitute("c", 0.6).Substitute("d", 1.3);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
foreach (var at in new[] { -2.4, -1.5, 0.3, 0.8, 1.5, 2.4 })
{
var want = original.Substitute("x", at).EvalNumerical();
var got = derivative.Substitute("x", at).EvalNumerical();
Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart)
< 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)),
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
}
}
}
}
Loading