Skip to content

Commit 1566841

Browse files
Read half-odd powers of a hyperbolic sine beside an exponential as a derivative (#2054)
Rubi's 6.7.1 e^(a + b x) sinh(c + d x)^(3/2) - 3/4 e^(a + b x) d^2/ ((b^2 - 9/4 d^2) sqrt(sinh(c + d x))) was declined: each term alone is not elementary, and the sum is the derivative of e^(a + b x) (4 b sinh^(3/2) - 6 d cosh sinh^(1/2))/(4 b^2 - 9 d^2). With E = e^(a + b x), S = sinh(u), C = cosh(u), u = c + d x, (E S^p C^q)' = E (b S^p C^q + d p S^(p - 1) C^(q + 1) + d q S^(p + 1) C^(q - 1)) with C^2 = 1 + S^2, so the answer is a sum of E S^p C^q over the integrand's monomials and the partners their derivative reaches, and its coefficients are a linear system: square, one row per monomial of the ansatz, with each compound coefficient of the integrand named while it is solved, since the elimination with symbols declined a row reducing to a zero it could not decide. Powers of one sine multiply on every branch, and no power of a power is taken. Last in the dispatch; checked by its derivative. 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 13471c4 commit 1566841

4 files changed

Lines changed: 205 additions & 0 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,19 @@ interval without a case for each. Only where nothing else answers the integrand
665665
| `"acoth(x)^2/(1-x^2)^2".ToEntity().Integrate("x")` | `integral(...)` | 1,304 characters, with `(ln((x + 1)/(x - 1)) - ln((1 + x)/(1 - x)))/2` for `K` |
666666
| `"acoth(c*x)^2/(1-c^2*x^2)^2".ToEntity().Integrate("x")` | `integral(...)` | 1,681 characters |
667667

668+
### Half-odd powers of a hyperbolic sine beside an exponential are read as a derivative
669+
670+
**An answer where there was none.** Rubi's 6.7.1 `e^(a + b x) sinh(c + d x)^(3/2) - 3/4 e^(a + b x) d^2/((b^2 - 9/4 d^2) sqrt(sinh(c + d x)))`
671+
was declined. Each of its terms alone is not elementary, and the sum is the derivative of
672+
`e^(a + b x) (4 b sinh^(3/2) - 6 d cosh sinh^(1/2))/(4 b^2 - 9 d^2)`. Such a sum is now read as the derivative of
673+
`e^(a + b x)` times a sum of `sinh^p cosh^q`, over the integrand's monomials and the ones their derivative reaches, with
674+
`cosh^2 = 1 + sinh^2`. Its coefficients are a linear system
675+
([#718](https://github.com/asc-community/AngouriMath/issues/718)).
676+
677+
| Input | Was (2.5.0) | Now |
678+
|---|---|---|
679+
| `"e^(a+b*x)*sinh(c+d*x)^(3/2)-3/4*e^(a+b*x)*d^2/((b^2-9/4*d^2)*sqrt(sinh(c+d*x)))".ToEntity().Integrate("x")` | `integral(...)` | 249 characters |
680+
668681
### A function of the hyperbolic tangent alone is integrated under `u = tanh`
669682

670683
**Answers where there were none.** `tanh(x)/sqrt(a + b tanh(x)^2 + c tanh(x)^4)` was declined, and so were 35 more of

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

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29948,6 +29948,148 @@ node is Powf(var @base, var exponent) && @base == MathS.e && powers.TryGetValue(
2994829948
return Folded(written.Substitute(constant, named - s * slope * x)).Substitute(s, sign);
2994929949
}
2995029950

29951+
/// <summary>
29952+
/// An exponential of a linear times half-odd powers of a hyperbolic sine of another linear, and the
29953+
/// cosine to the first power beside them, as the derivative of the same kind of sum: with
29954+
/// <c>E = e^(a + b x)</c>, <c>S = sinh(u)</c>, <c>C = cosh(u)</c> and <c>u = c + d x</c>,
29955+
/// <c>(E S^p C^q)' = E (b S^p C^q + d p S^(p - 1) C^(q + 1) + d q S^(p + 1) C^(q - 1))</c>, with
29956+
/// <c>C^2 = 1 + S^2</c>, so F is a sum of <c>E S^p C^q</c> over the integrand's monomials and their
29957+
/// partners, and its coefficients a linear system. Rubi's 6.7.1
29958+
/// <c>e^(a + b x) sinh(c + d x)^(3/2) - 3/4 e^(a + b x) d^2/((b^2 - 9/4 d^2) sqrt(sinh(c + d x)))</c>
29959+
/// is <c>(e^(a + b x) (b sinh^(3/2) - 3/2 d cosh sinh^(1/2))/(b^2 - 9/4 d^2))'</c>, where each term
29960+
/// alone is not elementary and the split declined both. Powers of one sine multiply on every
29961+
/// branch, and no power of a power is taken. Checked by its derivative.
29962+
/// https://github.com/asc-community/AngouriMath/issues/718
29963+
/// </summary>
29964+
internal static Entity? SolveHalfOddPowersOfAHyperbolicSineBesideAnExponentialAsADerivative(Entity expr, Entity.Variable x)
29965+
{
29966+
if (!Integration.AnsweringTheQuestionAsked)
29967+
return null;
29968+
var terms = Sumf.LinearChildren(expr);
29969+
if (terms.Count < 2)
29970+
return null;
29971+
// u: the argument of the first e^u - e^(-u) or e^u + e^(-u) in the integrand.
29972+
Entity? u = null;
29973+
foreach (var node in expr.Nodes)
29974+
if (node is Minusf or Sumf && node.DirectChildren.ToList() is [Powf(var e1, var p1), Powf(var e2, var p2)]
29975+
&& e1 == MathS.e && e2 == MathS.e && TreeAnalyzer.TryGetPolyLinear(p1, x, out var s1, out var c1) && !TreeAnalyzer.IsZero(s1)
29976+
&& TreeAnalyzer.TryGetPolyLinear(p2, x, out var s2, out var c2)
29977+
&& Functions.PartialFractions.InLowestTermsOverTheSymbols(s1 + s2) == Number.Integer.Zero
29978+
&& Functions.PartialFractions.InLowestTermsOverTheSymbols(c1 + c2) == Number.Integer.Zero)
29979+
{
29980+
u = p1;
29981+
break;
29982+
}
29983+
if (u is null || !TreeAnalyzer.TryGetPolyLinear(u, x, out var d, out _))
29984+
return null;
29985+
bool IsPlusMinus(Entity first, Entity second) => first is Powf(var e1, var p1) && e1 == MathS.e && p1 == u
29986+
&& second is Powf(var e2, var p2) && e2 == MathS.e && Functions.PartialFractions.InLowestTermsOverTheSymbols(p2 + u) == Number.Integer.Zero;
29987+
Entity? exponentOfE = null;
29988+
// (constant, power of S, power of C), the exponential's exponent kept apart.
29989+
(Entity K, ERational P, int Q)? Read(Entity node)
29990+
{
29991+
if (!node.ContainsNode(x))
29992+
return (node, ERational.Zero, 0);
29993+
switch (node)
29994+
{
29995+
case Minusf(var a, var b) when IsPlusMinus(a, b):
29996+
return (Number.Integer.Create(2), ERational.One, 0);
29997+
case Sumf(var a, var b) when IsPlusMinus(a, b):
29998+
return (Number.Integer.Create(2), ERational.Zero, 1);
29999+
case Powf(var e0, var exponent) when e0 == MathS.e && TreeAnalyzer.TryGetPolyLinear(exponent, x, out _, out _):
30000+
if (exponentOfE is null)
30001+
exponentOfE = exponent;
30002+
return exponentOfE == exponent ? (Number.Integer.One, ERational.Zero, 0) : null;
30003+
case Mulf(var a, var b):
30004+
return Read(a) is var (ka, pa, qa) && Read(b) is var (kb, pb, qb) ? (ka * kb, pa.Add(pb), qa + qb) : null;
30005+
case Divf(var a, var b):
30006+
return Read(a) is var (ka2, pa2, qa2) && Read(b) is var (kb2, pb2, qb2) ? (ka2 / kb2, pa2.Subtract(pb2), qa2 - qb2) : null;
30007+
// A power of the sine alone, or a whole power of anything read.
30008+
case Powf(var @base, Number.Integer { EInteger: var whole }) when whole.CanFitInInt32() && Read(@base) is var (k, pw, qw):
30009+
return (MathS.Pow(k, Number.Integer.Create(whole)), pw.Multiply(ERational.FromEInteger(whole)), qw * whole.ToInt32Unchecked());
30010+
case Powf(var @base, Number.Rational { ERational: var r }) when Read(@base) is var (k2, ps, qs) && qs == 0 && ps.Equals(ERational.One):
30011+
return (MathS.Pow(k2, Number.Rational.Create(r)), r, 0);
30012+
default:
30013+
return null;
30014+
}
30015+
}
30016+
// The integrand's monomials, C^2 written 1 + S^2.
30017+
var target = new Dictionary<(ERational, int), Entity>();
30018+
void Add(Dictionary<(ERational, int), Entity> into, ERational p, int q, Entity coefficient)
30019+
{
30020+
while (q >= 2)
30021+
{
30022+
Add(into, p.Add(ERational.FromInt32(2)), q - 2, coefficient);
30023+
q -= 2;
30024+
}
30025+
if (q < 0)
30026+
return;
30027+
into[(p, q)] = into.TryGetValue((p, q), out var already) ? already + coefficient : coefficient;
30028+
}
30029+
var halfOdd = false;
30030+
foreach (var term in terms)
30031+
{
30032+
if (Read(term) is not var (k, p, q) || q < 0 || q > 3)
30033+
return null;
30034+
halfOdd |= !p.Denominator.Equals(EInteger.One);
30035+
Add(target, p, q, k);
30036+
}
30037+
if (!halfOdd || exponentOfE is null || !TreeAnalyzer.TryGetPolyLinear(exponentOfE, x, out var b, out _))
30038+
return null;
30039+
// The ansatz: every monomial and its partner, which the derivative reaches.
30040+
var basis = new List<(ERational P, int Q)>();
30041+
foreach (var (p, q) in target.Keys)
30042+
foreach (var candidate in q == 0 ? new[] { (p, 0), (p.Subtract(ERational.One), 1) } : new[] { (p, 1), (p.Add(ERational.One), 0) })
30043+
if (!basis.Contains(candidate))
30044+
basis.Add(candidate);
30045+
var derivatives = basis.Select(m =>
30046+
{
30047+
var into = new Dictionary<(ERational, int), Entity>();
30048+
Add(into, m.P, m.Q, b);
30049+
if (!m.P.IsZero)
30050+
Add(into, m.P.Subtract(ERational.One), m.Q + 1, d * Number.Rational.Create(m.P));
30051+
if (m.Q > 0)
30052+
Add(into, m.P.Add(ERational.One), m.Q - 1, d * m.Q);
30053+
return into;
30054+
}).ToList();
30055+
// Square, one row for each monomial of the ansatz, which holds every one of the integrand's: the
30056+
// rows past it, the lowest power the derivative reaches, are zero for a solution, which an
30057+
// elimination with symbols does not decide; the derivative is checked at the end instead.
30058+
var keys = basis.Select(m => (m.P, m.Q)).ToList();
30059+
var matrix = keys.Select(key => derivatives.Select(map => map.TryGetValue(key, out var v) ? v.InnerSimplified : Number.Integer.Zero).ToArray()).ToArray();
30060+
// Each compound coefficient of the integrand a name while the system is solved: the elimination
30061+
// with symbols reduces a row to `0 = c` with c zero only as a value, `-3/4 d^2/(b^2 - 9/4 d^2)`
30062+
// less `d/2` times the second unknown, and declines.
30063+
var named = new List<(Variable Name, Entity Value)>();
30064+
Entity Named(Entity value)
30065+
{
30066+
if (value is Number or Variable)
30067+
return value;
30068+
var name = Variable.CreateUnique(expr + named.Aggregate((Entity)Number.Integer.Zero, (all, pair) => all + pair.Name), "k_es");
30069+
named.Add((name, value));
30070+
return name;
30071+
}
30072+
var rhs = keys.Select(key => target.TryGetValue(key, out var v) ? Named(v.InnerSimplified) : Number.Integer.Zero).ToArray();
30073+
if (!Functions.PartialFractions.TrySolveLinearWithSymbols(matrix, rhs, out var values))
30074+
return null;
30075+
var sine = (MathS.Pow(MathS.e, u) - MathS.Pow(MathS.e, -u)) / 2;
30076+
var cosine = (MathS.Pow(MathS.e, u) + MathS.Pow(MathS.e, -u)) / 2;
30077+
Entity sum = Number.Integer.Zero;
30078+
for (var j = 0; j < basis.Count; j++)
30079+
{
30080+
var value = Functions.PartialFractions.InLowestTermsOverTheSymbols(named.Aggregate(values[j], (all, pair) => all.Substitute(pair.Name, pair.Value)));
30081+
if (value == Number.Integer.Zero)
30082+
continue;
30083+
var (p, q) = basis[j];
30084+
Entity monomial = p.IsZero ? Number.Integer.One : MathS.Pow(sine, Number.Rational.Create(p));
30085+
if (q == 1)
30086+
monomial *= cosine;
30087+
sum += value * monomial;
30088+
}
30089+
var answer = MathS.Pow(MathS.e, exponentOfE) * sum;
30090+
return Functions.PartialFractions.DerivativeHoldsAtSampledPoints(answer, expr, x) ? answer : null;
30091+
}
30092+
2995130093
/// <summary>
2995230094
/// The sign of a real-valued function of <paramref name="x"/> among the factors of the
2995330095
/// integrand is constant on every interval between its zeros, and goes in front of the

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,6 +1801,9 @@ internal static bool ASumOverRootsWouldAnswer
18011801
// a sign on. Last, so that every one answered as written keeps that answer: parts answers a
18021802
// polynomial beside it.
18031803
if ((answer = IndefiniteIntegralSolver.SolveByNamingAnInverseSineOfASineLinearOnEachInterval(expr, x, integrateByParts)) is { }) return answer;
1804+
// And half-odd powers of a hyperbolic sine beside an exponential as the derivative of the same kind,
1805+
// last: each term alone is not elementary, and the split declined them.
1806+
if ((answer = IndefiniteIntegralSolver.SolveHalfOddPowersOfAHyperbolicSineBesideAnExponentialAsADerivative(expr, x)) is { }) return answer;
18041807
return null;
18051808
}
18061809
/// <summary>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
/// Half-odd powers of a hyperbolic sine beside an exponential of another linear, read as the
16+
/// derivative of the same kind of sum: Rubi's 6.7.1
17+
/// <c>e^(a + b x) sinh(c + d x)^(3/2) - 3/4 e^(a + b x) d^2/((b^2 - 9/4 d^2) sqrt(sinh(c + d x)))</c>
18+
/// is <c>(e^(a + b x) (4 b sinh^(3/2) - 6 d cosh sinh^(1/2))/(4 b^2 - 9 d^2))'</c>, and each term alone
19+
/// is not elementary, so the split declined both.
20+
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
21+
/// </summary>
22+
[Trait("Area", "Calculus")]
23+
public sealed class HalfOddPowersOfAHyperbolicSineBesideAnExponentialIntegralTest
24+
{
25+
[Theory]
26+
// Rubi 6.7.1:1141.
27+
[InlineData("e^(a + b*x)*sinh(c + d*x)^(3/2) - 3/4*e^(a + b*x)*d^2/((b^2 - 9/4*d^2)*sqrt(sinh(c + d*x)))")]
28+
public void IsAnsweredAsADerivative(string integrand)
29+
{
30+
var integral = integrand.ToEntity().Integrate("x");
31+
var text = integral.Stringize();
32+
Assert.DoesNotContain("integral(", text);
33+
Entity Pinned(Entity e) => e.Substitute("a", 0.4).Substitute("b", 1.3).Substitute("c", 0.2).Substitute("d", 0.7);
34+
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
35+
var original = Pinned(integrand.ToEntity());
36+
foreach (var at in new[] { 0.3, 0.9, 1.4, 2.1, 3.3 })
37+
{
38+
var want = original.Substitute("x", at).EvalNumerical();
39+
var got = derivative.Substitute("x", at).EvalNumerical();
40+
Assert.False(got.IsNaN, $"d/dx of the antiderivative of {integrand} has no value at x = {at}, where the integrand is {want}");
41+
Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart)
42+
< 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)),
43+
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
44+
}
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)