Skip to content

Commit a0fca42

Browse files
Rafael-SOWNetclaude
andcommitted
Read x times a rational function of an exponential of a logarithm off the integrand
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. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
1 parent ef4150e commit a0fca42

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
@@ -588,6 +588,23 @@ integrand over `D^2` as `(N/D)'`, with `N` a constant times each exponential in
588588
| `"(b+a*cosh(x))/(a+b*cosh(x))^2".ToEntity().Integrate("x")` | `integral(...)` | 70 characters, where before this change master gave 7,631 |
589589
| `"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 |
590590

591+
### x times a rational function of an exponential of a logarithm is read off the integrand
592+
593+
**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
594+
`(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
595+
`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
596+
exponentials alone, a rational G is now solved for by sampling w: a linear system, with the symbolic `b n` making
597+
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
598+
answers 6.5.3's `sech(a - ln(c x^n)/(n (p - 2)))^p` and its cosecant twin
599+
([#233](https://github.com/asc-community/AngouriMath/issues/233)).
600+
601+
| Input | Was (2.5.0) | Now |
602+
|---|---|---|
603+
| `"(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 |
604+
| `"-(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 |
605+
| `"sech(a-ln(c*x^n)/(n*(-2+p)))^p".ToEntity().Integrate("x")` | `integral(...)` | 311 characters, x times `sech(u)^(p - 2)` |
606+
| `"csch(a-ln(c*x^n)/(n*(-2+p)))^p".ToEntity().Integrate("x")` | `integral(...)` | 354 characters |
607+
591608
### A hyperbolic function of `a + 2 ln(x)` is integrated with `e^a` as a symbol too
592609

593610
**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
@@ -43286,6 +43286,170 @@ Entity WithTheExponentials(Entity e, Entity? one)
4328643286
}
4328743287
}
4328843288

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