Skip to content

Commit 56648c3

Browse files
Integrate a power of x times a hyperbolic function of a power under that power (#1971)
x^m F(sinh(c + d x^n), cosh(c + d x^n)), with n >= 2 dividing m + 1, is taken under t = x^n: x^m dx is t^(k - 1) dt/n for k = (m + 1)/n, and the question in t is the same function of a linear in t beside a whole power of t, which the rules for those answer. The parser writes the hyperbolic functions as exponentials of c + d x^n, which the substitution search did not read as a function of x^n. Rubi's 6.1.3, 6.2.3, 6.5.2 and 6.6.2, seven rows with x sech(a + b x^2)^7 among them, were past the budget. 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 05695e6 commit 56648c3

4 files changed

Lines changed: 187 additions & 0 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5926,6 +5926,27 @@ exponential over the derivative of its sine or cosine is now asked as a rational
59265926
| `"exp(n*sinh(a + b*x))*sinh(2*(a + b*x))".ToEntity().Integrate("x")` | `integral(...)` | elementary in `sinh(a + b x)` and its exponential |
59275927
| `"exp(n*cosh(1/2*(a + b*x)))*sinh(a + b*x)".ToEntity().Integrate("x")` | `integral(...)` | the same in `cosh((a + b x)/2)` |
59285928

5929+
### A power of x times a hyperbolic function of a power of x is integrated under that power
5930+
5931+
**Answers where there were none.** `x sech(a + b x^2)^7`, `x/(a + b sech(c + d x^2))^2` and their cosecant twins, Rubi's
5932+
6.5.2 and 6.6.2, `sinh(a + b x^2)^3/x^3` and its cosine, 6.1.3 and 6.2.3, and `x^5 cosh(a + b x^3)^7 sinh(a + b x^3)`,
5933+
6.7.1, were declined. The library writes the hyperbolic functions as exponentials, and the rules before this one spent
5934+
the budget on a power of a quotient of exponentials of `a + b x^2`. With `x^m` beside a rational function of hyperbolic
5935+
functions of `c + d x^n`, `n` at least 2 and dividing `m + 1`, the integrand is now asked in `t = x^n`, where
5936+
`x^m dx` is `t^(k - 1) dt/n` for `k = (m + 1)/n`, exactly wherever it is defined since the powers are whole. Only with
5937+
a whole power past the first of a sum or a quotient of the exponentials among them: `x/(a + b sech(c + d x^2))` and
5938+
`x^3 sinh(a + b x^2)` are answered as written, in fewer characters
5939+
([#233](https://github.com/asc-community/AngouriMath/issues/233)).
5940+
5941+
| Input | Was (2.5.0) | Now |
5942+
|---|---|---|
5943+
| `"x*sech(a + b*x^2)^7".ToEntity().Integrate("x")` | `integral(...)` | 494 characters |
5944+
| `"x/(a + b*sech(c + d*x^2))^2".ToEntity().Integrate("x")` | `integral(...)` | 2,193 characters |
5945+
| `"sinh(a + b*x^2)^3/x^3".ToEntity().Integrate("x")` | `integral(...)` | 480 characters |
5946+
| `"x^5*cosh(a + b*x^3)^7*sinh(a + b*x^3)".ToEntity().Integrate("x")` | `integral(...)` | 2,824 characters |
5947+
| `"x*(a + b*sech(c + d*x^2))^2".ToEntity().Integrate("x")` | `integral(...)`; 205 characters, in 8.9 s, on the unreleased master | 185 characters, in 45 ms |
5948+
| `"x/(cosh(a + b*x^2)^2 + 1)".ToEntity().Integrate("x")` | `integral(...)`; 145 characters, in 8.3 s, on the unreleased master | 231 characters, in 76 ms |
5949+
59295950
### A hyperbolic function of a logarithm is integrated, the exponent folded structurally
59305951

59315952
`tanh(ln(x))` was left as written. The library spells `tanh(y)` with `e^(2y)`, so a hyperbolic

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

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27949,6 +27949,109 @@ when two.Equals(EInteger.FromInt32(2)) && e1 == MathS.e && e2 == MathS.e
2794927949
return answer.Nodes.Any(node => node == MathS.NaN) ? null : answer;
2795027950
}
2795127951

27952+
/// <summary>
27953+
/// A power of x times a rational function of hyperbolic functions of a linear in a whole
27954+
/// power of x, <c>x^m F(sinh(c + d x^n), cosh(c + d x^n))</c> with <c>n &gt;= 2</c> dividing
27955+
/// <c>m + 1</c>, under <c>t = x^n</c>: <c>x^m dx</c> is <c>t^(k - 1) dt/n</c> for
27956+
/// <c>k = (m + 1)/n</c>, and the question in <c>t</c> is the same function of a linear in
27957+
/// <c>t</c> beside a whole power of <c>t</c>.
27958+
/// </summary>
27959+
/// <remarks>
27960+
/// The library writes the hyperbolic functions as exponentials, so <c>x sech(a + b x^2)^7</c>
27961+
/// arrives as x beside a seventh power of a quotient of exponentials of <c>a + b x^2</c>, and
27962+
/// the rules before this one spent the budget on it: Rubi's 6.5.2 and 6.6.2
27963+
/// <c>x sech(a + b x^2)^7</c>, <c>x/(a + b sech(c + d x^2))^2</c> and the two with the
27964+
/// cosecant, 6.1.3 and 6.2.3 <c>sinh(a + b x^2)^3/x^3</c> and its cosine, and 6.7.1
27965+
/// <c>x^5 cosh(a + b x^3)^7 sinh(a + b x^3)</c>, each of which in <c>t</c> is answered in
27966+
/// under a second. Exact wherever the integrand is defined, since the powers are whole:
27967+
/// <c>x^m</c> is <c>x^(n - 1) (x^n)^(k - 1)</c> at every <c>x</c> but 0. Exponentials of a
27968+
/// linear in <c>x^n</c> and whole powers of them only, with a whole power past the first of
27969+
/// a sum or a quotient of them among them: <c>x/(a + b sech(c + d x^2))</c> and
27970+
/// <c>x^3 sinh(a + b x^2)</c> are answered as written, in fewer characters than in
27971+
/// <c>t</c>, and are left to that. After the rules that answer a polynomial beside
27972+
/// exponentials of a quadratic as written, which it leaves as they were; a symbolic
27973+
/// <c>n</c> is <see cref="SolveByAPowerOfTheVariableTimesAFunctionOfItsPower"/>'s.
27974+
/// https://github.com/asc-community/AngouriMath/issues/233
27975+
/// </remarks>
27976+
internal static Entity? SolveAPowerOfXTimesAHyperbolicFunctionOfAPowerUnderThatPower(Entity expr, Entity.Variable x, bool integrateByParts)
27977+
{
27978+
if (!Integration.AnsweringTheQuestionAsked)
27979+
return null;
27980+
var m = 0;
27981+
Entity constant = Number.Integer.One;
27982+
Entity? above = null, below = null;
27983+
foreach (var (factor, underneath) in FactorsOfTheIntegrand(expr))
27984+
{
27985+
if (!factor.ContainsNode(x))
27986+
constant = underneath ? constant / factor : constant * factor;
27987+
else if (factor == x)
27988+
m += underneath ? -1 : 1;
27989+
else if (factor is Powf(var b0, Number.Integer { EInteger: var p0 }) && b0 == x && p0.CanFitInInt32())
27990+
m += underneath ? -p0.ToInt32Unchecked() : p0.ToInt32Unchecked();
27991+
else if (underneath)
27992+
below = below is null ? factor : below * factor;
27993+
else
27994+
above = above is null ? factor : above * factor;
27995+
}
27996+
if (above is null && below is null)
27997+
return null;
27998+
var rest = below is null ? above! : (above ?? Number.Integer.One) / below;
27999+
// The one whole power of x the rest is written in.
28000+
var powers = rest.Nodes.Where(node => node is Powf(var b1, Number.Integer) && b1 == x).Distinct().ToList();
28001+
if (powers.Count != 1 || powers[0] is not Powf(_, Number.Integer { EInteger: var power }) || !power.CanFitInInt32())
28002+
return null;
28003+
var n = power.ToInt32Unchecked();
28004+
if (n < 2 || n > 12 || (m + 1) % n != 0)
28005+
return null;
28006+
var t = Variable.CreateUnique(expr, "t_hyp");
28007+
var inT = rest.Substitute(powers[0], t);
28008+
if (inT.ContainsNode(x))
28009+
return null;
28010+
// Exponentials of a linear in t with a constant base, and whole powers, sums,
28011+
// products and quotients of them: nothing else of t. With a whole power past the
28012+
// first of a sum of them, or of a quotient: a hyperbolic function to the first power
28013+
// is answered in x as written, by the rules before this one, and in fewer characters.
28014+
var anExponential = false;
28015+
var aWholePower = false;
28016+
foreach (var node in inT.Nodes)
28017+
{
28018+
if (!node.ContainsNode(t))
28019+
continue;
28020+
switch (node)
28021+
{
28022+
case Variable or Sumf or Minusf or Mulf or Divf:
28023+
continue;
28024+
case Powf(var sum, Number.Integer { EInteger: var whole }):
28025+
aWholePower |= sum is Sumf or Minusf or Mulf or Divf && whole.Abs().CompareTo(EInteger.One) > 0;
28026+
continue;
28027+
case Powf(var @base, var exponent) when !@base.ContainsNode(t)
28028+
&& TreeAnalyzer.TryGetPolyLinear(exponent, t, out var slope, out _) && !slope.ContainsNode(t)
28029+
&& !Functions.PartialFractions.IsZeroAsAValue(slope):
28030+
anExponential = true;
28031+
continue;
28032+
default:
28033+
return null;
28034+
}
28035+
}
28036+
if (!anExponential || !aWholePower)
28037+
return null;
28038+
// A bare t outside every exponential is a power of x that n does not reach.
28039+
if (inT.Replace(node => node is Powf(var b2, var e2) && !b2.ContainsNode(t) && e2.ContainsNode(t) ? Number.Integer.One : node).ContainsNode(t))
28040+
return null;
28041+
var k = (m + 1) / n;
28042+
var question = k switch
28043+
{
28044+
1 => inT,
28045+
> 1 => MathS.Pow(t, Number.Integer.Create(k - 1)) * inT,
28046+
_ => inT / MathS.Pow(t, Number.Integer.Create(1 - k)),
28047+
};
28048+
if (Integration.ComputeAsAQuestionOfItsOwn(question, t, integrateByParts) is not { } integrated
28049+
|| integrated.Nodes.Any(node => node is Integralf))
28050+
return null;
28051+
var answer = (constant / Number.Integer.Create(n) * integrated.Substitute(t, powers[0])).InnerSimplified;
28052+
return answer.Nodes.Any(node => node == MathS.NaN) ? null : answer;
28053+
}
28054+
2795228055
/// <summary>
2795328056
/// A product of whole powers of a sine and a cosine of one linear times something else in x
2795428057
/// that is no polynomial, <c>sin(u)^3 cos(u)^6/(a + b u)</c>, linearised: the product is a sum

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,10 @@ private static Entity WithItsPiecewiseSumsCombined(Entity expr)
883883
if ((answer = IndefiniteIntegralSolver.SolveByTheDerivativeOfAQuadraticAsTheVariable(expr, x, integrateByParts)) is { }) return answer;
884884
// x^(n - 1) g(x^n) with a symbolic n is g(u)/n under u = x^n.
885885
if ((answer = IndefiniteIntegralSolver.SolveByAPowerOfTheVariableTimesAFunctionOfItsPower(expr, x, integrateByParts)) is { }) return answer;
886+
// And x^(k n - 1) g(x^n) with a whole n >= 2, for a whole power of hyperbolic functions of
887+
// a linear in x^n, which arrive as exponentials: `x sech(a + b x^2)^7` is
888+
// `sech(a + b t)^7/2` under t = x^2, where the rules before this spent the budget.
889+
if ((answer = IndefiniteIntegralSolver.SolveAPowerOfXTimesAHyperbolicFunctionOfAPowerUnderThatPower(expr, x, integrateByParts)) is { }) return answer;
886890
// A whole power of a product of a constant and the variable, as the product of
887891
// the powers, which is how the inverse hyperbolic secant and cosecant arrive.
888892
if ((answer = IndefiniteIntegralSolver.SolveByDistributingWholePowersOfProducts(expr, x, integrateByParts)) is { }) return answer;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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+
/// A power of x times a whole power of hyperbolic functions of a linear in <c>x^n</c>, under
16+
/// <c>t = x^n</c>, where <c>x^m dx</c> is <c>t^((m + 1)/n - 1) dt/n</c>. The hyperbolic functions
17+
/// arrive as exponentials of <c>a + b x^n</c>, and in x the rules spent the budget on each of
18+
/// these. Rubi's 6.1.3, 6.2.3, 6.5.2, 6.6.2 and 6.7.1.
19+
/// <a href="https://github.com/asc-community/AngouriMath/issues/233">#233</a>
20+
/// </summary>
21+
[Trait("Area", "Calculus")]
22+
public sealed class APowerOfXTimesAHyperbolicFunctionOfAPowerIntegralTest
23+
{
24+
[Theory]
25+
[InlineData("sinh(a + b*x^2)^3/x^3")]
26+
[InlineData("cosh(a + b*x^2)^3/x^3")]
27+
[InlineData("x*sech(a + b*x^2)^7")]
28+
[InlineData("x*csch(a + b*x^2)^7")]
29+
[InlineData("x/(a + b*sech(c + d*x^2))^2")]
30+
[InlineData("x/(a + b*csch(c + d*x^2))^2")]
31+
[InlineData("x*(a + b*sech(c + d*x^2))^2")]
32+
[InlineData("x^5*cosh(a + b*x^3)^7*sinh(a + b*x^3)")]
33+
[InlineData("sinh(a + b*x^2)^4/x^5")]
34+
[InlineData("x/(cosh(a + b*x^2)^2 + 1)")]
35+
public void UnderThePower(string integrand)
36+
{
37+
var integral = integrand.ToEntity().Integrate("x");
38+
var text = integral.Stringize();
39+
Assert.DoesNotContain("integral(", text);
40+
Assert.True(text.Length < 5000, $"{text.Length} characters of answer for {integrand}");
41+
Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7).Substitute("c", 0.4).Substitute("d", 0.3);
42+
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
43+
var original = Pinned(integrand.ToEntity());
44+
var compared = 0;
45+
foreach (var at in new[] { -1.3, -0.8, -0.4, 0.35, 0.9, 1.4 })
46+
{
47+
var want = original.Substitute("x", at).EvalNumerical();
48+
var got = derivative.Substitute("x", at).EvalNumerical();
49+
if (want.IsNaN)
50+
continue;
51+
compared++;
52+
Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart)
53+
< 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)),
54+
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
55+
}
56+
Assert.True(compared >= 5, $"only {compared} points could be compared for {integrand}");
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)