Skip to content

Commit de11aaf

Browse files
Read x times a rational function of an exponential of a logarithm off the integrand (#2047)
Rubi's 6.5.3 (1 - b^2 n^2) sech(u) + 2 b^2 n^2 sech(u)^3 for u = a + b ln(c x^n) is (x (-sech(u) - b n tanh(u) sech(u)))', and its 6.6.3 twin in the cosecant the same way; both were declined. With w = e^u the Euler derivative x w' is b n w, so (x G(w))' = G + b n w G'(w): the integrand P/Q in w is that for G = N/Q exactly where N Q + c w (N' Q - N Q') = P Q, linear in N's coefficients, read at sampled values of w and solved with the symbols in; the homogeneous solution w^(-1/c) is no rational function for a symbolic c, so the solution is unique. Only where x stands in the exponentials alone and the exponent's Euler derivative is a constant, at the question asked; the quotient is simplified in w to lowest terms and the answer checked. A symbolic power K B^p of one function of w goes through x K B^(p - 2) H(w), since (x B^(p - 2) H)' = B^(p - 2) (H + c w H' + (p - 2) c (w B'/B) H): Rubi's 6.5.3 sech(a - ln(c x^n)/(n (p - 2)))^p, whose c is -1/(p - 2), is x (p - 2)/(p - 1) sech(u)^(p - 2) (1 - tanh(u)), and its cosecant twin in 6.6.3 the same way. There the system is square, (p - 2) c is simplified once, every row is scaled by c's denominator and the entries taken bare: with 1/(p - 2) and a provided not p - 2 = 0 in them, the elimination over p did not settle. Part of #233. Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent e30f63f commit de11aaf

4 files changed

Lines changed: 233 additions & 0 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,23 @@ integrand over `D^2` as `(N/D)'`, with `N` a constant times each exponential in
618618
| `"(b+a*cosh(x))/(a+b*cosh(x))^2".ToEntity().Integrate("x")` | `integral(...)` | 70 characters, where before this change master gave 7,631 |
619619
| `"sech(x)^2*tanh(x)^2/(2+tanh(x)^3)^2".ToEntity().Integrate("x")` | no answer in thirty seconds | 60 characters, `-1/3/(2 + tanh(x)^3)`, in 55 ms; master gave 117 in 16 s and 9.7 GB |
620620

621+
### x times a rational function of an exponential of a logarithm is read off the integrand
622+
623+
**Answers where there were none.** Rubi's 6.5.3 `(1 - b^2 n^2) sech(u) + 2 b^2 n^2 sech(u)^3` for `u = a + b ln(c x^n)` is
624+
`(x (-sech(u) - b n tanh(u) sech(u)))'`, and its 6.6.3 twin in the cosecant the same way. Both were declined. With
625+
`w = e^u`, the Euler derivative `x w' = b n w` makes `(x G(w))'` equal `G + b n w G'(w)`. Where x stands in the
626+
exponentials alone, a rational G is now solved for by sampling w: a linear system, with the symbolic `b n` making
627+
its solution unique. A symbolic power `B^p` of one function of w goes through `x B^(p - 2) H(w)` the same way, which
628+
answers 6.5.3's `sech(a - ln(c x^n)/(n (p - 2)))^p` and its cosecant twin
629+
([#233](https://github.com/asc-community/AngouriMath/issues/233)).
630+
631+
| Input | Was (2.5.0) | Now |
632+
|---|---|---|
633+
| `"(1-b^2*n^2)*sech(a+b*ln(c*x^n))+2*b^2*n^2*sech(a+b*ln(c*x^n))^3".ToEntity().Integrate("x")` | `integral(...)` | 235 characters |
634+
| `"-(1-b^2*n^2)*csch(a+b*ln(c*x^n))+2*b^2*n^2*csch(a+b*ln(c*x^n))^3".ToEntity().Integrate("x")` | `integral(...)` | 247 characters |
635+
| `"sech(a-ln(c*x^n)/(n*(-2+p)))^p".ToEntity().Integrate("x")` | `integral(...)` | 311 characters, x times `sech(u)^(p - 2)` |
636+
| `"csch(a-ln(c*x^n)/(n*(-2+p)))^p".ToEntity().Integrate("x")` | `integral(...)` | 354 characters |
637+
621638
### A hyperbolic function of `a + 2 ln(x)` is integrated with `e^a` as a symbol too
622639

623640
**Answers where there were none.** `tanh(a + 2 ln(x))^2/x^3` was declined, and so were nineteen more of Rubi's 6.3.2,

‎Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs‎

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43291,6 +43291,170 @@ Entity WithTheExponentials(Entity e, Entity? one)
4329143291
}
4329243292
}
4329343293

43294+
/// <summary>
43295+
/// x times a rational function of an exponential whose exponent has a constant Euler
43296+
/// derivative, <c>x g'(x) = c</c>, read off the integrand: Rubi's 6.5.3
43297+
/// <c>(1 - b^2 n^2) sech(u) + 2 b^2 n^2 sech(u)^3</c> for <c>u = a + b ln(c x^n)</c> is
43298+
/// <c>(x (-sech(u) - b n tanh(u) sech(u)))'</c>, and was declined.
43299+
/// https://github.com/asc-community/AngouriMath/issues/233
43300+
/// </summary>
43301+
/// <remarks>
43302+
/// With <c>w = e^g</c>, <c>x w' = c w</c>, so <c>(x G(w))' = G + c w G'(w)</c>: the integrand
43303+
/// <c>P/Q</c> in w is that for <c>G = N/Q</c> exactly where
43304+
/// <c>N Q + c w (N' Q - N Q') = P Q</c>, linear in N's coefficients and read at sampled values
43305+
/// of w. A symbolic c makes the solution unique, the homogeneous one being <c>w^(-1/c)</c>. Only
43306+
/// where x stands in the exponentials alone, the others whole powers of the first, and checked.
43307+
/// </remarks>
43308+
internal static Entity? SolveAsXTimesARationalFunctionOfAnExponentialOfALogarithm(Entity expr, Entity.Variable x)
43309+
{
43310+
if (!Integration.AnsweringTheQuestionAsked)
43311+
return null;
43312+
static bool IsAnExponential(Entity node, Entity.Variable x) => node is Powf(var @base, var exponent) && @base == MathS.e && exponent.ContainsNode(x);
43313+
if (expr.Replace(node => IsAnExponential(node, x) ? Number.Integer.One : node).ContainsNode(x))
43314+
return null;
43315+
var exponentials = expr.Nodes.Where(node => IsAnExponential(node, x)).Distinct().ToList();
43316+
if (exponentials.Count is 0 or > 4)
43317+
return null;
43318+
var first = ((Powf)exponentials[0]).Exponent;
43319+
// Only an exponent of a logarithm: a linear one is the exponential substitution's.
43320+
if (TreeAnalyzer.TryGetPolyLinear(first, x, out _, out _) || !first.Nodes.Any(node => node is Logf))
43321+
return null;
43322+
var rate = Functions.PartialFractions.Bare((x * first.Differentiate(x)).Simplify());
43323+
if (rate.ContainsNode(x) || Functions.PartialFractions.IsZeroAsAValue(rate))
43324+
return null;
43325+
var powers = new Dictionary<Entity, int>();
43326+
foreach (var exponential in exponentials)
43327+
if (Functions.PartialFractions.Bare((((Powf)exponential).Exponent / first).Simplify()) is Number.Integer { EInteger: var whole } && whole.CanFitInInt32())
43328+
powers[exponential] = whole.ToInt32Unchecked();
43329+
else
43330+
return null;
43331+
var w = Variable.CreateUnique(expr, "w_euler");
43332+
var inW = expr.Replace(node => powers.TryGetValue(node, out var k) ? MathS.Pow(w, Number.Integer.Create(k)) : node);
43333+
var exponentialOfTheFirst = exponentials.First(e => powers[e] == 1);
43334+
// A symbolic power of one function of w, `K B^p`: x K B^(p - 2) H(w) for a rational H, since
43335+
// (x B^(p - 2) H)' is B^(p - 2) (H + c w H' + (p - 2) c (w B'/B) H), and H solves the system
43336+
// below with that term beside. Rubi's 6.5.3 `sech(a - ln(c x^n)/(n (p - 2)))^p` has
43337+
// c = -1/(p - 2), and is `x (p - 2)/(p - 1) sech(u)^(p - 2) (1 - tanh(u))`.
43338+
Entity? symbolicPower = null, ofIt = null;
43339+
Entity constantFactor = Number.Integer.One;
43340+
foreach (var (factor, underneath) in FactorsOfTheIntegrand(inW))
43341+
{
43342+
if (!factor.ContainsNode(w))
43343+
{
43344+
constantFactor = underneath ? constantFactor / factor : constantFactor * factor;
43345+
continue;
43346+
}
43347+
if (underneath || symbolicPower is not null || factor is not Powf(var b, var power) || power.ContainsNode(w) || power is Number)
43348+
{
43349+
symbolicPower = null;
43350+
ofIt = null;
43351+
break;
43352+
}
43353+
(ofIt, symbolicPower) = (b, power);
43354+
}
43355+
if (symbolicPower is not null && ofIt is not null)
43356+
{
43357+
var squared = Functions.SingleQuotient.Of(Functions.SingleQuotient.Combine(MathS.Sqr(ofIt)));
43358+
var logarithmic = (w * ofIt.Differentiate(w) / ofIt).InnerSimplified;
43359+
if (!TreeAnalyzer.TryGetPolynomial(Functions.PartialFractions.Bare(squared.Denominator.Expand()), w, out var ofTheSquare)
43360+
|| ofTheSquare.Keys.Any(k => k.Sign < 0) || ofTheSquare.Keys.Max()! is not { } top || !top.CanFitInInt32() || top.ToInt32Unchecked() is < 1 or > 12)
43361+
return null;
43362+
var denominatorOfH = squared.Denominator;
43363+
var count = top.ToInt32Unchecked() + 1;
43364+
var derivativeOfTheDenominator = denominatorOfH.Differentiate(w);
43365+
// `(p - 2) c` once and simplified, which is -1 for the rows this is for: inside each entry
43366+
// `(p - 2) (-1)/(p - 2)` was left standing, and the elimination did not resolve it.
43367+
var scaledRate = Functions.PartialFractions.Bare(((symbolicPower - 2) * rate).Simplify());
43368+
// And every row times the rate's denominator, so that the entries are polynomials in the
43369+
// symbols: over `p - 2` the elimination did not settle the system.
43370+
var (rateAbove, rateBelow) = Functions.SingleQuotient.Of(rate);
43371+
// Square: with p in the entries the elimination is symbolic, and the rows past the
43372+
// unknowns are rational functions of p it cannot decide are zero; the answer is checked.
43373+
var points = Enumerable.Range(0, count).Select(k => (Entity)Number.Rational.Create(EInteger.FromInt32(2 + 3 * k), EInteger.FromInt32(1 + k % 3))).ToList();
43374+
var rows = new Entity[points.Count][];
43375+
var wanted = new Entity[points.Count];
43376+
for (var r = 0; r < points.Count; r++)
43377+
{
43378+
var at = points[r];
43379+
var qAt = denominatorOfH.Substitute(w, at).InnerSimplified;
43380+
var qPrimeAt = derivativeOfTheDenominator.Substitute(w, at).InnerSimplified;
43381+
var lAt = logarithmic.Substitute(w, at).InnerSimplified;
43382+
rows[r] = new Entity[count];
43383+
// w^j (Q + c j Q - c w Q' + (p - 2) c L Q).
43384+
for (var j = 0; j < count; j++)
43385+
// Bare: `(p - 2) c` is `-1 provided not p - 2 = 0` inner-simplified, and the elimination
43386+
// does not read a condition.
43387+
rows[r][j] = Functions.PartialFractions.Bare((MathS.Pow(at, Number.Integer.Create(j)) * (rateBelow * qAt + rateAbove * j * qAt - rateAbove * at * qPrimeAt + rateBelow * scaledRate * lAt * qAt)).Expand().InnerSimplified);
43388+
wanted[r] = Functions.PartialFractions.Bare((rateBelow * MathS.Sqr(ofIt).Substitute(w, at) * qAt * qAt).Expand().InnerSimplified);
43389+
}
43390+
if (!Functions.PartialFractions.TrySolveLinearWithSymbols(rows, wanted, out var ofH))
43391+
return null;
43392+
Entity aboveH = Number.Integer.Zero;
43393+
for (var j = 0; j < count; j++)
43394+
{
43395+
var coefficient = LowestOverTheSymbols(ofH[j]);
43396+
if (!Functions.PartialFractions.IsZeroAsAValue(coefficient))
43397+
aboveH += coefficient * MathS.Pow(w, Number.Integer.Create(j));
43398+
}
43399+
if (aboveH == Number.Integer.Zero)
43400+
return null;
43401+
var h = Functions.PartialFractions.Bare((aboveH / denominatorOfH).Simplify());
43402+
var withThePower = Functions.PartialFractions.Bare((x * constantFactor * MathS.Pow(ofIt, symbolicPower - 2) * h).Substitute(w, exponentialOfTheFirst).InnerSimplified);
43403+
return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(withThePower, expr, x) ? withThePower : null;
43404+
}
43405+
var (above, below) = Functions.SingleQuotient.Of(Functions.SingleQuotient.Combine(inW));
43406+
if (!TreeAnalyzer.TryGetPolynomial(Functions.PartialFractions.Bare(above.Expand()), w, out var p) || !TreeAnalyzer.TryGetPolynomial(Functions.PartialFractions.Bare(below.Expand()), w, out var q))
43407+
return null;
43408+
// Negative powers of w cleared from both.
43409+
var lowest = p.Keys.Concat(q.Keys).Min()!;
43410+
Entity Written(Dictionary<EInteger, Entity> read)
43411+
{
43412+
Entity sum = Number.Integer.Zero;
43413+
foreach (var pair in read)
43414+
sum += pair.Value * MathS.Pow(w, Number.Integer.Create(pair.Key - lowest));
43415+
return sum.InnerSimplified;
43416+
}
43417+
var bigP = Written(p);
43418+
var bigQ = Written(q);
43419+
var degreeOfQ = q.Keys.Max()! - lowest;
43420+
if (!degreeOfQ.CanFitInInt32() || degreeOfQ.ToInt32Unchecked() is < 1 or > 12)
43421+
return null;
43422+
var size = degreeOfQ.ToInt32Unchecked() + 1;
43423+
var derivativeOfQ = bigQ.Differentiate(w);
43424+
var samples = Enumerable.Range(0, size + 3).Select(k => (Entity)Number.Rational.Create(EInteger.FromInt32(2 + 3 * k), EInteger.FromInt32(1 + k % 3))).ToList();
43425+
var matrix = new Entity[samples.Count][];
43426+
var rhs = new Entity[samples.Count];
43427+
for (var r = 0; r < samples.Count; r++)
43428+
{
43429+
var at = samples[r];
43430+
var qAt = bigQ.Substitute(w, at).InnerSimplified;
43431+
var qPrimeAt = derivativeOfQ.Substitute(w, at).InnerSimplified;
43432+
matrix[r] = new Entity[size];
43433+
// w^j (Q + c j Q - c w Q').
43434+
for (var j = 0; j < size; j++)
43435+
matrix[r][j] = Functions.PartialFractions.Bare((MathS.Pow(at, Number.Integer.Create(j)) * (qAt + rate * j * qAt - rate * at * qPrimeAt)).InnerSimplified);
43436+
rhs[r] = Functions.PartialFractions.Bare((bigP.Substitute(w, at) * qAt).InnerSimplified);
43437+
}
43438+
if (!Functions.PartialFractions.TrySolveLinearWithSymbols(matrix, rhs, out var coefficients))
43439+
return null;
43440+
Entity numerator = Number.Integer.Zero;
43441+
for (var j = 0; j < size; j++)
43442+
{
43443+
var coefficient = LowestOverTheSymbols(coefficients[j]);
43444+
if (!Functions.PartialFractions.IsZeroAsAValue(coefficient))
43445+
numerator += coefficient * MathS.Pow(w, Number.Integer.Create(j));
43446+
}
43447+
if (numerator == Number.Integer.Zero)
43448+
return null;
43449+
// In lowest terms: N is found over the whole of Q, of which the antiderivative needs only a
43450+
// power the less of each repeated factor -- simplified in w, a quotient of polynomials of
43451+
// degree twelve at most, since the cancellation over the rationals does not take the
43452+
// symbols in N: for the sech row, 385 characters, and 235 simplified.
43453+
var quotient = Functions.PartialFractions.Bare((numerator / bigQ).Simplify());
43454+
var answer = Functions.PartialFractions.Bare((x * quotient).Substitute(w, exponentialOfTheFirst).InnerSimplified);
43455+
return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
43456+
}
43457+
4329443458
/// <summary>
4329543459
/// <c>x^m (a + b atan(c x))^p</c> over a power of <c>d (1 +- i c x)</c>, under <c>x = tan(u)/c</c>, where
4329643460
/// <c>1 + i tan(u)</c> is <c>e^(i u)/cos(u)</c>: the integrand in <c>u</c> is

‎Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,9 @@ internal static bool ASumOverRootsWouldAnswer
725725
// parts. The split and the exponential substitution answered `(b - a sinh(x))/(a + b sinh(x))^2`
726726
// with a piecewise on a discriminant, and parts beside x ran past the budget integrating it.
727727
if ((answer = IndefiniteIntegralSolver.SolveAsTheDerivativeOfAQuotientOverASumOfExponentials(expr, x, integrateByParts)) is { }) return answer;
728+
// And x times a rational function of an exponential of a logarithm, read off the integrand by
729+
// the Euler derivative: `(1 - b^2 n^2) sech(u) + 2 b^2 n^2 sech(u)^3` for `u = a + b ln(c x^n)`.
730+
if ((answer = IndefiniteIntegralSolver.SolveAsXTimesARationalFunctionOfAnExponentialOfALogarithm(expr, x)) is { }) return answer;
728731
// A cosine beside a function of the sine with a power off the rationals in it, in the sine:
729732
// the substitution search declined `cos(x)/(sin(x) + sin(x)^sqrt(2))`.
730733
if ((answer = IndefiniteIntegralSolver.SolveACosineBesideAFunctionOfTheSineWithAnIrrationalPowerInIt(expr, x, integrateByParts)) is { }) return answer;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//
2+
// Copyright (c) 2019-2026 Angouri.
3+
// AngouriMath is licensed under MIT.
4+
// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
5+
// Website: https://am.angouri.org.
6+
//
7+
8+
using System;
9+
using AngouriMath.Extensions;
10+
using Xunit;
11+
12+
namespace AngouriMath.Tests.Calculus
13+
{
14+
/// <summary>
15+
/// x times a rational function of <c>e^u</c> for <c>u = a + b ln(c x^n)</c>, read off the integrand by
16+
/// the Euler derivative <c>x u' = b n</c>: Rubi's 6.5.3 <c>(1 - b^2 n^2) sech(u) + 2 b^2 n^2 sech(u)^3</c>
17+
/// is <c>(x (-sech(u) - b n tanh(u) sech(u)))'</c>, and its 6.6.3 twin in the cosecant the same way;
18+
/// both were declined.
19+
/// <a href="https://github.com/asc-community/AngouriMath/issues/233">#233</a>
20+
/// </summary>
21+
[Trait("Area", "Calculus")]
22+
public sealed class XTimesARationalFunctionOfAnExponentialOfALogarithmIntegralTest
23+
{
24+
[Theory]
25+
[InlineData("(1 - b^2*n^2)*sech(a + b*ln(c*x^n)) + 2*b^2*n^2*sech(a + b*ln(c*x^n))^3")]
26+
[InlineData("-(1 - b^2*n^2)*csch(a + b*ln(c*x^n)) + 2*b^2*n^2*csch(a + b*ln(c*x^n))^3")]
27+
// And a symbolic power, through x times the power two less: Rubi's 6.5.3 and 6.6.3.
28+
[InlineData("sech(a - ln(c*x^n)/(n*(-2 + p)))^p")]
29+
[InlineData("csch(a - ln(c*x^n)/(n*(-2 + p)))^p")]
30+
public void IsXTimesTheQuotient(string integrand)
31+
{
32+
var integral = integrand.ToEntity().Integrate("x");
33+
var text = integral.Stringize();
34+
Assert.DoesNotContain("integral(", text);
35+
Assert.True(text.Length < 400, $"the antiderivative of {integrand} is {text.Length} characters");
36+
Entity Pinned(Entity e) => e.Substitute("a", 0.4).Substitute("b", 0.7).Substitute("c", 1.3).Substitute("n", 1.6).Substitute("p", 3.4);
37+
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
38+
var original = Pinned(integrand.ToEntity());
39+
foreach (var at in new[] { 0.4, 0.9, 1.3, 2.2, 3.1 })
40+
{
41+
var want = original.Substitute("x", at).EvalNumerical();
42+
var got = derivative.Substitute("x", at).EvalNumerical();
43+
Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart)
44+
< 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)),
45+
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
46+
}
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)