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
12 changes: 12 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17193,6 +17193,63 @@ private static bool IsARationalFunctionOfExponentials(Entity expr, Entity.Variab
}
}

/// <summary>
/// An exponential of a polynomial beside the polynomial's derivative, with the rest a
/// function of the polynomial: <c>G^P k P' f(P)</c> is <c>k G^u f(u)</c> under <c>u = P</c>.
/// Rubi's 2.3, <c>e^(a + b x + c x^2) (b + 2 c x) (a + b x + c x^2)^(n/2)</c>.
/// https://github.com/asc-community/AngouriMath/issues/1501
/// </summary>
/// <remarks>
/// <see cref="SolveBySubstitution"/> reads this <c>u</c> and does not reach it: for its
/// own search it writes an exponential of a sum as a product of exponentials, so that
/// <c>e^(e^x) e^x</c> is <c>e^u du</c>, and <c>e^(a + b x + c x^2)</c> is then
/// <c>e^a e^(b x) e^(c x^2)</c>, with no <c>P</c> 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.
/// </remarks>
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);
}

/// <summary>
/// A factor of <paramref name="expr"/>, read through products and quotients, that is a
/// constant to a polynomial in <paramref name="x"/> of degree two or more, or <see langword="null"/>.
/// </summary>
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
};

/// <summary>
/// Attempts to solve an integral using u-substitution.
/// Looks for patterns where f(g(x)) * g'(x) can be integrated as F(g(x)).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
13 changes: 13 additions & 0 deletions Sources/Tests/UnitTests/Calculus/GaussianIntegralTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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"));

/// <summary>
/// An exponential of a quadratic beside the quadratic's derivative and a half-odd power of
/// the quadratic, under <c>u = a + b x + c x^2</c>: <c>e^u u^(n/2)</c>. Rubi's 2.3. The
/// quadratic is positive at every point for these pins.
/// </summary>
[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"));

/// <summary>
/// An odd negative power ends at <c>int e^(A x^2)/x</c>, which is the exponential
/// integral, so it is not taken.
Expand Down
Loading