Skip to content

Commit 46a56fb

Browse files
Read the symbolic trigonometric power as a factor of the integrand (#1958)
The rule for `f^m (alpha + beta f^2)` as the derivative of a product, for a symbolic m and f a sine, cosine, secant or cosecant, wrote 1 for the first such power anywhere in the tree. A power below the bar or inside one term of a sum was taken for a factor of the integrand, and five such integrands came back wrong on master; 2.5.0 declined them. The rule now reads its power among the integrand's factors, so one under the bar is `f^(-m)`. It also takes a number that is not whole, which the identities hold for on the principal branch, as in Rubi's 4.5.4.1 `(3 + 3 sec(c + d x)^2)/sqrt(sec(c + d x))`. 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 27e6514 commit 46a56fb

3 files changed

Lines changed: 70 additions & 9 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -910,13 +910,20 @@ in Rubi's 1.1.3.4 come out shorter as well, and those that took two seconds take
910910
of Rubi's 4.1.4.1, 4.2.4.1 and 4.5.4.1. `f^m (alpha + beta f^2)` for `f` a sine, cosine, secant or cosecant of a linear
911911
is the derivative of `cos sin^(m + 1)`, `sin cos^(m + 1)`, `tan sec^m` or `cot csc^m` where its bracket is a multiple of
912912
that derivative's, and is now integrated so ([#718](https://github.com/asc-community/AngouriMath/issues/718)).
913+
The power is read as a factor of the integrand, so one under the bar is `f^(-m)`: `(m + (1 - m) sec^2)/sec^m` is
914+
the derivative of `tan sec^(-m)`. And `m` may be a number that is not whole, as in Rubi's 4.5.4.1
915+
`(3 + 3 sec(c + d x)^2)/sqrt(sec(c + d x))`.
913916

914917
| Input | Was (2.5.0) | Now |
915918
|---|---|---|
916919
| `"sec(c+d*x)^m*(-q*m/(1+m)+q*sec(c+d*x)^2)".ToEntity().Integrate("x")` | `integral(...)` | 75 characters |
917920
| `"sec(c+d*x)^m*(A-A*(1+m)*sec(c+d*x)^2/m)".ToEntity().Integrate("x")` | `integral(...)` | 54 characters |
918921
| `"sec(k+f*x)^m*(m-(1+m)*sec(k+f*x)^2)".ToEntity().Integrate("x")` | `integral(...)` | 54 characters |
919922
| `"sin(k+f*x)^m*(1+m-(2+m)*sin(k+f*x)^2)".ToEntity().Integrate("x")` | `integral(...)` | 71 characters |
923+
| `"(m+(1-m)*sec(c+d*x)^2)/sec(c+d*x)^m".ToEntity().Integrate("x")` | `integral(...)` | 56 characters |
924+
| `"(-(1-m)+(2-m)*cos(x)^2)/cos(x)^m".ToEntity().Integrate("x")` | `integral(...)` | 55 characters |
925+
| `"(3+3*sec(c+d*x)^2)/sec(c+d*x)^(1/2)".ToEntity().Integrate("x")` | `integral(...)` | 60 characters |
926+
| `"sqrt(sin(x))*(3/2-5/2*sin(x)^2)".ToEntity().Integrate("x")` | `integral(...)` | 27 characters |
920927

921928
### A power of a sine or hyperbolic sine over a binomial in a power of it is split over the binomial's roots
922929

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

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31310,8 +31310,8 @@ Piecewise piecewise when piecewise.Cases.Any() => piecewise.Cases.First().Expres
3131031310
}
3131131311

3131231312
/// <summary>
31313-
/// <c>f^m (alpha + beta f^2)</c> for a symbolic <c>m</c> and <c>f</c> a sine, cosine, secant or
31314-
/// cosecant of a linear <c>u</c>, where it is the derivative of a product: with <c>u' = 1</c>,
31313+
/// <c>f^m (alpha + beta f^2)</c> for a symbolic <c>m</c>, or a number that is not whole, and <c>f</c> a
31314+
/// sine, cosine, secant or cosecant of a linear <c>u</c>, where it is the derivative of a product: with <c>u' = 1</c>,
3131531315
/// <c>(cos sin^(m + 1))' = sin^m ((m + 1) - (m + 2) sin^2)</c>,
3131631316
/// <c>(sin cos^(m + 1))' = cos^m ((m + 2) cos^2 - (m + 1))</c>,
3131731317
/// <c>(tan sec^m)' = sec^m ((m + 1) sec^2 - m)</c> and <c>(cot csc^m)' = -csc^m ((m + 1) csc^2 - m)</c>.
@@ -31321,23 +31321,39 @@ Piecewise piecewise when piecewise.Cases.Any() => piecewise.Cases.First().Expres
3132131321
/// square terms, <c>alpha + beta f^2</c>, which matches <c>p0 + p2 f^2</c> above where
3132231322
/// <c>beta p0 = alpha p2</c>; the answer is <c>(alpha/p0) G/u'</c>. Rubi's 4.1.4.1, 4.2.4.1 and 4.5.4.1
3132331323
/// <c>sec(c + d x)^m (m - (1 + m) sec(c + d x)^2)</c> and the like were declined.
31324+
/// The power is one of the integrand's factors, and one under the bar is <c>f^(-m)</c>: only then is the
31325+
/// integrand <c>f^m</c> times the rest, and a power in one term of a sum, as in
31326+
/// <c>-m sec^m + (1 + m) sec^2</c>, is not a factor of it.
3132431327
/// https://github.com/asc-community/AngouriMath/issues/718
3132531328
/// </remarks>
3132631329
internal static Entity? SolveASymbolicPowerOfATrigonometricFunctionAsTheDerivativeOfAProduct(Entity expr, Entity.Variable x)
3132731330
{
3132831331
if (!Integration.AnsweringTheQuestionAsked)
3132931332
return null;
31330-
// The power with a symbol in its exponent, of a sine, cosine, secant or cosecant of a linear.
31331-
var power = expr.Nodes.OfType<Powf>().FirstOrDefault(node => node.Exponent.Vars.Any() && !node.Exponent.ContainsNode(x)
31332-
&& node.Base is Sinf or Cosf or Secantf or Cosecantf);
31333-
if (power is not Powf(var function, var m))
31334-
return null;
31333+
// The power with a symbol in its exponent, of a sine, cosine, secant or cosecant of a linear,
31334+
// as a factor of the integrand.
31335+
var factors = FactorsOfTheIntegrand(expr);
31336+
var at = factors.FindIndex(pair => pair.Factor is Powf(Sinf or Cosf or Secantf or Cosecantf, var exponent)
31337+
&& (exponent.Vars.Any() && !exponent.ContainsNode(x)
31338+
// Or a number that is not whole: the derivatives hold for any m on the principal branch,
31339+
// and a whole one is left to the rules for whole powers.
31340+
|| exponent is Number.Rational and not Number.Integer));
31341+
if (at < 0 || factors[at].Factor is not Powf(var function, var written))
31342+
return null;
31343+
var m = factors[at].Underneath ? (-written).InnerSimplified : written;
3133531344
var argument = function switch { Sinf(var y) => y, Cosf(var y) => y, Secantf(var y) => y, Cosecantf(var y) => y, _ => null };
3133631345
if (argument is null || !TreeAnalyzer.TryGetPolyLinear(argument, x, out var slope, out _) || Functions.PartialFractions.IsZeroAsAValue(slope))
3133731346
return null;
3133831347
var s = Variable.CreateUnique(expr, "s_trig");
31339-
// The power taken off by writing 1 for it: `s^m (...)/s^m` is not cancelled by the simplification.
31340-
var inS = expr.Substitute(power, Number.Integer.One).Substitute(function, s).InnerSimplified;
31348+
// The rest of the factors, without the power: `s^m (...)/s^m` is not cancelled by the simplification.
31349+
Entity above = Number.Integer.One, below = Number.Integer.One;
31350+
for (var i = 0; i < factors.Count; i++)
31351+
if (i != at)
31352+
{
31353+
if (factors[i].Underneath) below *= factors[i].Factor;
31354+
else above *= factors[i].Factor;
31355+
}
31356+
var inS = (above / below).Substitute(function, s).InnerSimplified;
3134131357
if (inS.ContainsNode(x) || !TreeAnalyzer.TryGetPolynomial(inS.Expand(), s, out var read)
3134231358
|| read.Keys.Any(key => !key.IsZero && !key.Equals(EInteger.FromInt32(2))))
3134331359
return null;

‎Sources/Tests/UnitTests/Calculus/ASymbolicPowerOfATrigonometricFunctionAsADerivativeIntegralTest.cs‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ private static void DifferentiatesBack(string integrand, params (string, double)
2424
{
2525
var integral = integrand.ToEntity().Integrate("x");
2626
Assert.DoesNotContain("integral(", integral.Stringize());
27+
DifferentiatesBack(integrand, integral, pins);
28+
}
2729

30+
private static void DifferentiatesBack(string integrand, Entity integral, params (string, double)[] pins)
31+
{
2832
var pinned = integral.Substitute("C", 0);
2933
Entity original = integrand.ToEntity();
3034
foreach (var (name, value) in pins)
@@ -52,12 +56,46 @@ private static void DifferentiatesBack(string integrand, params (string, double)
5256
[InlineData("sin(c+d*x)^m*(1+m-(2+m)*sin(c+d*x)^2)")]
5357
[InlineData("cos(x)^m*(-(1+m)+(2+m)*cos(x)^2)")]
5458
[InlineData("csc(x)^m*(m-(1+m)*csc(x)^2)")]
59+
// Under the bar the power is f^(-m): (tan sec^(-m))' and (sin cos^(1 - m))'.
60+
[InlineData("(m+(1-m)*sec(c+d*x)^2)/sec(c+d*x)^m")]
61+
[InlineData("(-(1-m)+(2-m)*cos(x)^2)/cos(x)^m")]
5562
public void IsAnsweredAndDifferentiatesBack(string integrand)
5663
{
5764
DifferentiatesBack(integrand, ("A", 0.6), ("q", 1.25), ("c", 0.3), ("d", 0.75), ("m", 2.5));
5865
DifferentiatesBack(integrand, ("A", -0.75), ("q", 0.4), ("c", -1), ("d", -0.5), ("m", 1.0 / 3));
5966
}
6067

68+
// A power that is not whole: Rubi's 4.5.4.1 (3 + 3 sec(c + d x)^2)/sqrt(sec(c + d x)), and the
69+
// same derivatives at m = 1/2, 1/3 and 3/2.
70+
[Theory]
71+
[InlineData("(3+3*sec(c+d*x)^2)/sec(c+d*x)^(1/2)")]
72+
[InlineData("sqrt(sin(x))*(3/2-5/2*sin(x)^2)")]
73+
[InlineData("sin(c+d*x)^(1/3)*(4/3-7/3*sin(c+d*x)^2)")]
74+
[InlineData("cos(x)^(3/2)*(-5/2+7/2*cos(x)^2)")]
75+
[InlineData("csc(x)^(1/2)*(1/2-3/2*csc(x)^2)")]
76+
public void IsAnsweredForAPowerThatIsNotWhole(string integrand)
77+
{
78+
DifferentiatesBack(integrand, ("c", 0.3), ("d", 0.75));
79+
DifferentiatesBack(integrand, ("c", -1), ("d", -0.5));
80+
}
81+
82+
// The power is not a factor of the whole integrand, or is one under the bar, where the bracket is
83+
// not the derivative's: none of these is the derivative of a single product.
84+
[Theory]
85+
[InlineData("(-m+(1+m)*sec(x)^2)/sec(x)^m")]
86+
[InlineData("-m*sec(x)^m+(1+m)*sec(x)^2")]
87+
[InlineData("sin(x)^m*(1+m)-(2+m)*sin(x)^2")]
88+
[InlineData("(1+m-(2+m)*sin(x)^2)/sin(x)^m")]
89+
[InlineData("(-m+(1+m)*csc(x)^2)/csc(x)^m")]
90+
public void IsNotAnsweredWronglyWhereThePowerIsNotThatFactor(string integrand)
91+
{
92+
var integral = integrand.ToEntity().Integrate("x");
93+
if (integral.Stringize().Contains("integral("))
94+
return;
95+
DifferentiatesBack(integrand, integral, ("m", 2.5));
96+
DifferentiatesBack(integrand, integral, ("m", 1.0 / 3));
97+
}
98+
6199
[Fact]
62100
public void IsNotClaimedWhereTheBracketIsNotTheDerivative()
63101
{

0 commit comments

Comments
 (0)