Skip to content

Commit ef4150e

Browse files
Ask a hyperbolic quotient over a quadratic in its sine or cosine as the sine's, before the tangent (#2042)
Rubi's 6.7.1 sinh(x)^2/(a + b sinh(x) + c sinh(x)^2) ran past the budget in SolveARationalFunctionOfTheHyperbolicTangent: under the tangent of the half argument it is a quartic with symbols in it, and the partial fractions over it did not return. Under Osborn's rule it is a rational function of a sine over a symbolic quadratic in it, which SolveARationalFunctionOfASineOverASymbolicQuadratic answers at once; the Osborn rule asked it only after the exponential substitution had declined, and the tangent is before both. A mode of the Osborn rule now asks that one rule directly, cheap to decline, ahead of the tangent. 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 94c9ab9 commit ef4150e

4 files changed

Lines changed: 80 additions & 2 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,6 +1068,21 @@ roots, `1 - x^2`, is taken as its two ([#718](https://github.com/asc-community/A
10681068
| `"1/((1 - x^2)*(a + b*x^3))".ToEntity().Integrate("x")` | `integral(...)` | 1,600 characters |
10691069
| `"1/(x*(1 - x^2)*(a + b*x^3))".ToEntity().Integrate("x")` | `integral(...)` | 1,637 characters |
10701070

1071+
### A hyperbolic quotient over a quadratic in its sine or cosine is asked as the sine's, before the tangent
1072+
1073+
**Answers where there were none, and one longer spelling.** Rubi's 6.7.1 `sinh(x)^2/(a + b sinh(x) + c sinh(x)^2)`
1074+
and `sinh(x)^3/(a + b sinh(x) + c sinh(x)^2)` ran past the budget. The tangent of the half argument made each a
1075+
quartic with symbols in it, and its partial fractions did not return. Under Osborn's rule each is a rational
1076+
function of a sine over a symbolic quadratic in it, and the sine's rule for that shape is now asked first, before
1077+
the tangent. A quotient master answered some other way can come back spelled through the sine instead
1078+
([#233](https://github.com/asc-community/AngouriMath/issues/233)).
1079+
1080+
| Input | Was (2.5.0) | Now |
1081+
|---|---|---|
1082+
| `"sinh(x)^2/(a+b*sinh(x)+c*sinh(x)^2)".ToEntity().Integrate("x")` | no answer in twenty seconds | 681 characters |
1083+
| `"sinh(x)^3/(a+b*sinh(x)+c*sinh(x)^2)".ToEntity().Integrate("x")` | no answer in twenty seconds | 810 characters |
1084+
| `"cosh(x)^2/(a+b*cosh(x)+c*cosh(x)^2)".ToEntity().Integrate("x")` | no answer in twenty seconds; 791 characters on the unreleased master | 813 characters |
1085+
10711086
### A hyperbolic integrand is asked as a trigonometric one
10721087

10731088
**Answers where there were none.** The parser writes the hyperbolic functions as exponentials, so the trigonometric

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1429,7 +1429,8 @@ static bool APowerOfAQuotientOfLinearsVanishingAtPlusOrMinusOne(Entity inU, Vari
14291429
/// the bar of degree above two: the second pass answers it in <c>e^L</c>, and the question in
14301430
/// <c>s</c> took seconds to decline, five gigabytes for <c>1/(1 + sinh(x)^3)</c> against 0.2.
14311431
/// </remarks>
1432-
internal static Entity? SolveAHyperbolicIntegrandAsATrigonometricOne(Entity expr, Entity.Variable x, bool integrateByParts, bool rationalToo)
1432+
internal static Entity? SolveAHyperbolicIntegrandAsATrigonometricOne(Entity expr, Entity.Variable x, bool integrateByParts, bool rationalToo,
1433+
bool overAQuadraticInTheSine = false)
14331434
{
14341435
// Only the question asked: a question in s is not cheap to decline, and below the top every
14351436
// sub-question a route gives up on would ask it.
@@ -1497,7 +1498,11 @@ static bool APowerOfAQuotientOfLinearsVanishingAtPlusOrMinusOne(Entity inU, Vari
14971498
&& AFactorAboveAQuadraticInS(inS, s))
14981499
return null;
14991500
var question = MathS.i / slopeOfL * inS;
1500-
if (Integration.ComputeAsAQuestionOfItsOwn(question, s, integrateByParts) is not { } inTermsOfS
1501+
// Or the one rule for a rational function of a sine or a cosine over a symbolic quadratic in
1502+
// it, asked ahead of the exponential substitution and cheap to decline.
1503+
if ((overAQuadraticInTheSine
1504+
? SolveARationalFunctionOfASineOverASymbolicQuadratic(inS, s, integrateByParts) is { } overTheQuadratic ? MathS.i / slopeOfL * overTheQuadratic : null
1505+
: Integration.ComputeAsAQuestionOfItsOwn(question, s, integrateByParts)) is not { } inTermsOfS
15011506
|| inTermsOfS.Nodes.Any(node => node is Integralf || node == MathS.NaN))
15021507
return null;
15031508
var linear = interceptOfL.Evaled is Number.Complex { IsZero: true } ? slopeOfL * x : interceptOfL + slopeOfL * x;

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,6 +1234,11 @@ internal static bool ASumOverRootsWouldAnswer
12341234
// `cosh(x)^7/(a + b cosh(x)^2)` and `sech(x)^3/(a + b sinh(x))^2` are a second here
12351235
// and past the budget there.
12361236
if ((answer = IndefiniteIntegralSolver.SolveByBiochesOddHyperbolicSubstitution(expr, x, integrateByParts)) is { }) return answer;
1237+
// A rational function of e^L that Osborn's rule makes one of a sine or a cosine over a symbolic
1238+
// quadratic in it, asked as that before the tangent: under it Rubi's
1239+
// `sinh(x)^2/(a + b sinh(x) + c sinh(x)^2)` is a quartic with symbols, and its partial
1240+
// fractions ran past the budget, where the sine's rule answers the question in s at once.
1241+
if ((answer = IndefiniteIntegralSolver.SolveAHyperbolicIntegrandAsATrigonometricOne(expr, x, integrateByParts, rationalToo: true, overAQuadraticInTheSine: true)) is { }) return answer;
12371242
if ((answer = IndefiniteIntegralSolver.SolveARationalFunctionOfTheHyperbolicTangent(expr, x, integrateByParts)) is { }) return answer;
12381243
// A rational function of a sine or a cosine over a quadratic in it with a symbol
12391244
// among the coefficients, split over the quadratic's two roots: under the
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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 System.Threading;
10+
using System.Threading.Tasks;
11+
using AngouriMath.Extensions;
12+
using Xunit;
13+
14+
namespace AngouriMath.Tests.Calculus
15+
{
16+
/// <summary>
17+
/// A rational function of a hyperbolic sine or cosine over a symbolic quadratic in it, asked under
18+
/// Osborn's rule as one of a sine over a quadratic before the tangent of the half argument: under that
19+
/// tangent Rubi's 6.7.1 <c>sinh(x)^2/(a + b sinh(x) + c sinh(x)^2)</c> is a quartic with symbols in it,
20+
/// and its partial fractions ran past the budget.
21+
/// <a href="https://github.com/asc-community/AngouriMath/issues/233">#233</a>
22+
/// </summary>
23+
[Trait("Area", "Calculus")]
24+
public sealed class AHyperbolicQuotientOverAQuadraticInItAsTheSinesIntegralTest
25+
{
26+
[Theory]
27+
[InlineData("sinh(x)^2/(a + b*sinh(x) + c*sinh(x)^2)")]
28+
[InlineData("sinh(x)^3/(a + b*sinh(x) + c*sinh(x)^2)")]
29+
[InlineData("cosh(x)^2/(a + b*cosh(x) + c*cosh(x)^2)")]
30+
public void IsAnswered(string integrand)
31+
{
32+
Entity? answer = null;
33+
// A thread of its own, so that the guard is not spent waiting behind other tests' work.
34+
var task = Task.Factory.StartNew(() => answer = integrand.ToEntity().Integrate("x"),
35+
CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
36+
Assert.True(task.Wait(LimitTermination.Guard), $"{integrand} did not finish within {LimitTermination.Guard}");
37+
var integral = answer!;
38+
Assert.DoesNotContain("integral(", integral.Stringize());
39+
Entity Pinned(Entity e) => e.Substitute("a", 1.3).Substitute("b", 0.7).Substitute("c", 0.4);
40+
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
41+
var original = Pinned(integrand.ToEntity());
42+
foreach (var at in new[] { -1.7, -0.6, 0.4, 1.3, 2.2 })
43+
{
44+
var want = original.Substitute("x", at).EvalNumerical();
45+
var got = derivative.Substitute("x", at).EvalNumerical();
46+
Assert.False(got.IsNaN, $"d/dx of the antiderivative of {integrand} has no value at x = {at}, where the integrand is {want}");
47+
Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart)
48+
< 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)),
49+
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
50+
}
51+
}
52+
}
53+
}

0 commit comments

Comments
 (0)