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
17 changes: 17 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4889,6 +4889,23 @@ now, and a closed interval at one point meets the other in that point or in noth
|---|---|---|
| `"(3; 1) /\ [0; 5]".ToEntity().InnerSimplified` | `[0; 5]` — wrong | `{ }` |

### Exponentials of linears in two bases over a power of x are integrated to the exponential integral

**Answers where there were none.** `a^x b^x/x`, `a^x b^x/x^2` and `a^x b^x/x^3`, Rubi's 2.3, were declined, and so was
`sinh(f x)^2/x`. The rules for an exponential over a power of x read one exponential, and no spelling of the integrand
merges two: `(a b)^x` is `e^(x ln(a b))`, and `ln(a b)` is not `ln(a) + ln(b)` for every `a` and `b`. A product of
exponentials of linears beside a negative power of x is now taken as one, `e^(r x)` with `r = ln(a) + ln(b)`, by parts
down to `Ei(r x)`. Where the rates cancel, as `e^(f x) e^(-f x)` does in `sinh(f x)^2`, the term is the power of x
alone, decided as a value rather than read off the written `f - f`. An exponent with a quadratic in it, or a sine
beside the exponentials, is declined as before ([#233](https://github.com/asc-community/AngouriMath/issues/233)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"a^x*b^x/x".ToEntity().Integrate("x")` | `integral(...)` | `Ei((ln(a) + ln(b)) x)`, 27 characters |
| `"a^x*b^x/x^3".ToEntity().Integrate("x")` | `integral(...)` | `e^(r x)` over `x^2` and over `x`, and `Ei(r x)`, 214 characters |
| `"a^(c+d*x)*b^(f+g*x)/x".ToEntity().Integrate("x")` | `integral(...)` | `e^(c ln(a) + f ln(b)) Ei((d ln(a) + g ln(b)) x)`, 65 characters |
| `"sinh(f*x)^2/x".ToEntity().Integrate("x")` | `integral(...)` | `Ei(2 f x)/4 - ln(x)/2 + Ei(-2 f x)/4`, 63 characters |

### A power of an exponential over a polynomial is integrated to the exponential integral

**Answers where there were none.** `(F^(g (e + f x)))^n` with a symbol for `F` or `n` was read by no rule
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8355,8 +8355,10 @@ void AddALinear(Entity linear, int power)
quadratics.Add((coefficient, a, b, c));
}
// Some exponent left a quadratic: with none, the product is the elementary
// exponential's, and the rules for that answer it without an imaginary unit.
if (!quadratics.Any(term => !TreeAnalyzer.IsZero(term.A)))
// exponential's, and the rules for that answer it without an imaginary unit -- except
// beside a negative power of x, below.
var aQuadratic = quadratics.Any(term => !TreeAnalyzer.IsZero(term.A));
if (!aQuadratic && trigonometrics > 0)
return null;
// The polynomial and the power of x together: as the product written, for the moments
// at a square's centre, which substitute into it as the integrand writes it, and as
Expand All @@ -8378,6 +8380,8 @@ void AddALinear(Entity linear, int power)
if (powers is not null && powers.Keys.Any(power => power.Abs().CompareTo(EInteger.FromInt32(MostPowerOfAPolynomialBesideExponentials)) > 0))
return null;
var aNegativePower = powers is not null && powers.Keys.Any(power => power.Sign < 0);
if (!aQuadratic && !aNegativePower)
return null;
Entity total = Number.Integer.Zero;
foreach (var (coefficient, a, b, c) in quadratics)
{
Expand All @@ -8392,9 +8396,39 @@ void AddALinear(Entity linear, int power)
else
integral = IntegralPatterns.APolynomialTimesTheGaussian(written, MathS.e, a, b, c, x);
}
// A negative power beside an elementary exponential is the exponential integral.
// A negative power beside an elementary exponential is the exponential integral:
// for exponentials alone, each power of x against e^(b x) by parts down to Ei(b x).
// The other rules read one exponential, and a^x b^x/x is two of different bases,
// which no spelling of the integrand merges: (a b)^x is e^(x ln(a b)), and
// ln(a b) is not ln(a) + ln(b) for every a and b.
else if (aNegativePower)
return null;
{
if (aQuadratic || powers is null)
return null;
// A rate that cancels, `b - b` from e^(b x) e^(-b x), is a power of x alone, and is
// decided as a value: written, it is not zero, and the exponential integral of it
// divided by it.
if (VanishesIdentically(b))
{
Entity powersIntegrated = Number.Integer.Zero;
foreach (var monomial in powers)
powersIntegrated += monomial.Value * (monomial.Key.Equals(EInteger.FromInt32(-1))
? MathS.Ln(x)
: MathS.Pow(x, Number.Integer.Create(monomial.Key + 1)) / Number.Integer.Create(monomial.Key + 1));
integral = MathS.Pow(MathS.e, c) * powersIntegrated;
total += coefficient * integral;
continue;
}
var exponential = MathS.Pow(MathS.e, b * x);
Entity elementary = Number.Integer.Zero, ei = Number.Integer.Zero;
foreach (var monomial in powers)
{
var (part, eiCoefficient) = APowerTimesAnExponential(monomial.Key.ToInt32Checked(), b, exponential, x);
elementary += monomial.Value * part;
ei += monomial.Value * eiCoefficient;
}
integral = MathS.Pow(MathS.e, c) * (elementary + ei * MathS.Ei(b * x));
}
else if (!TreeAnalyzer.IsZero(b))
integral = MathS.Pow(MathS.e, b * x + c) * APolynomialAgainstAnExponential(powers, b, x);
else
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// 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>
/// Exponentials of linears in different bases over a power of x: <c>a^x b^x/x</c> is
/// <c>Ei((ln(a) + ln(b)) x)</c>. The rule for a product of exponentials left every product
/// without a quadratic to the rules for one exponential, and beside a negative power of x none
/// of them reads two. Rubi's 2.3.
/// <a href="https://github.com/asc-community/AngouriMath/issues/233">#233</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class ExponentialsOfTwoBasesOverAPowerOfXIntegralTest
{
[Fact]
public void TheExponentialIntegralOfTheSumOfTheLogarithms()
=> Assert.Equal("Ei((ln(a) + ln(b)) * x)".ToEntity(), "a^x*b^x/x".ToEntity().Integrate("x").Substitute("C", 0).InnerSimplified);

[Theory]
[InlineData("a^x*b^x/x^2")]
[InlineData("a^x*b^x/x^3")]
[InlineData("a^x*b^x*(1 + x)/x^2")]
[InlineData("a^(c + d*x)*b^(f + g*x)/x")]
[InlineData("2^x*3^x/x")]
// e^(b x) e^(-b x) beside 1/x is 1/x, its rate b - b decided as a value: written, the rate is
// not zero, and these were answered with the exponential integral of it divided by it.
[InlineData("x*Shi(f*x)*sinh(f*x)")]
[InlineData("x*Chi(f*x)*cosh(f*x)")]
[InlineData("sinh(f*x)^2/x")]
public void ByPartsDownToTheExponentialIntegral(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
var text = integral.Stringize();
Assert.DoesNotContain("integral(", text);
Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7).Substitute("c", 0.4).Substitute("d", 0.3)
.Substitute("f", 2.3).Substitute("g", -0.6);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
var compared = 0;
foreach (var at in new[] { -0.9, -0.4, 0.3, 0.8, 1.3, 2.9 })
{
var want = original.Substitute("x", at).EvalNumerical();
var got = derivative.Substitute("x", at).EvalNumerical();
if (want.IsNaN)
continue;
compared++;
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}");
}
Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
}
}
}
Loading