diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index f2e47d4f4..8b5bbb404 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -307,6 +307,28 @@ either now; and the secant quotient was simplified to `cos/(b^(5/2) d)` through `(1/cos)^(3/2) = cos^(-3/2)`, which is wrong for every negative cosine and cancelled against the same wrong step on the denominator. On the 1774-problem independent suites this is 1707 to 1705, the two `asin` rows, with the sign error gone. +### A function comes out of a fractional power of its even power with its sign + +**Improvement, not silent.** The entry two above made `(sin(x)^2)^(3/2)` the modulus `|sin(x)|^3` +and left three of Rubi's integrands unevaluated for it, and answered two more in powers of the +modulus through the substitutions. The modulus is the function times its sign, and the sign is a +constant between the function's zeros: `(a sin(x)^2)^(5/2)` is `a^(5/2) sgn(sin(x)) sin(x)^5`, the +sign comes out in front of the integral, and what is left is a whole power any rule reads. An even +power of a real function is not negative, so `(c q)^p = c^p q^p` holds for the principal powers +whatever `c` is, and nothing is assumed about the constant — checked at `a = -3` as well as +`a = 2`. Whole products of the exponents only, at the top of the integration only, and after the +rule that does the same for a polynomial under a square root, whose answers are as they were. +[#1387](https://github.com/asc-community/AngouriMath/issues/1387) + +| | Was | Is | +|---|---|---| +| `"x*sqrt(sin(x)^2)".Integrate("x")` | unevaluated | `sgn(sin(x)) (sin(x) - x cos(x))` | +| `"(csc(x)^2)^(3/2)".Integrate("x")`, `"1/(csc(x)^2)^(7/2)".Integrate("x")` | unevaluated | `sgn(csc(x))` times the integral of `csc^3`, of `sin^7` | +| `"(sin(x)^2)^(3/2)".Integrate("x")` | `(2/3 tan(x/2)^5 + 2 tan(x/2)^3) ((2 tan(x/2))^2)^(1/2)/((tan(x/2)^2 + 1)^3)` | `sgn(sin(x)) (cos(x)^3/3 - cos(x))` | +| `"(a*sin(x)^2)^(5/2)".Integrate("x")` | `sgn(sin(x)) a^(5/2) (...)` in `(1 - abs(sin(x))^2)^(1/2)` over `sgn(cos(x))` | `sgn(sin(x)) a^(5/2) (2 cos(x)^3/3 - cos(x) - cos(x)^5/5)` | +| `"1/sqrt(a*cot(x)^2)".Integrate("x")` | `-sgn(tan(x)) ln(1/sqrt(1 + abs(tan(x))^2))/sqrt(a)` | `sgn(cot(x)) ln(1 + tan(x)^2)/(2 sqrt(a))` | +| `"(b*tan(x)^2)^(5/2)".Integrate("x")` | `sgn(tan(x)) b^(5/2) ((abs(tan(x))^2)^2/2 - abs(tan(x))^2 + ln(abs(tan(x))^2 + 1))/2` | the same with `tan(x)` for `abs(tan(x))` | + ### A condition the answer states on its own is no longer repeated beside it **Improvement, not silent.** `Simplify` returned `(1 - ln(x))/x^2 provided not x = 0` for the diff --git a/Sources/.editorconfig b/Sources/.editorconfig index 786bdbae9..6e3890d8a 100644 --- a/Sources/.editorconfig +++ b/Sources/.editorconfig @@ -442,6 +442,9 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed [Tests/UnitTests/Calculus/CombinedRadicalsTest.cs] file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n +[Tests/UnitTests/Calculus/EvenPowerSignTest.cs] +file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n + [Tests/UnitTests/Calculus/RothsteinTragerTest.cs] file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs index 89eda82a9..e98517cb2 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/IndefiniteIntegralSolver.cs @@ -11371,6 +11371,82 @@ bool IsAConstantTimesAPowerOfOneMinusUSquared(Entity polynomial, out Entity cons return answer.Nodes.Any(node => node == MathS.NaN) ? null : answer; } + /// + /// A fractional power of a constant times an even power of a function of x, the + /// function taken out of the power with its sign: (a sin(x)^2)^(5/2) is + /// a^(5/2) |sin(x)|^5, which is a^(5/2) sgn(sin(x)) sin(x)^5 -- the sign + /// a constant between the sine's zeros, carried through the integration as a symbol + /// and written back. Rubi's x sqrt(sin(x)^2) is sgn(sin(x)) (sin(x) - x cos(x)) + /// that way, and 1/(csc(x)^2)^(7/2) is sgn(csc(x)) times the seventh power + /// of the sine, where the substitutions read the root of the square as a modulus and + /// answered in powers of it. + /// + /// + /// An even power of a real function is not negative, so (c q)^p = c^p q^p holds + /// for the principal powers whatever c is, and (f^(2k))^p is |f|^(2kp). + /// Whole products of the exponents only, so that what is handed on is a whole power of + /// the function; a polynomial radicand is the rule above's, which comes first. At the + /// top only, as there: a substitution's variable is one it knows the sign of, and a + /// sign written for it is one nothing differentiates. + /// https://github.com/asc-community/AngouriMath/issues/718 + /// + internal static Entity? SolveByTakingAFunctionOutOfAPowerOfItsEvenPower(Entity expr, Entity.Variable x, bool integrateByParts) + { + if (!Integration.AnsweringTheQuestionAsked) + return null; + Entity signs = Number.Integer.One; + var rewritten = expr.Replace(node => + { + if (node is not Powf(var radicand, Number.Rational exponent) || exponent is Number.Integer || !radicand.ContainsNode(x)) + return node; + // The radicand read as a constant times even whole powers of functions of x, + // each power whole once multiplied by the exponent; anything else is not this + // rule's. + Entity constant = Number.Integer.One; + var functions = new List<(Entity function, EInteger power)>(); + var top = radicand; + if (radicand is Divf(var above, var below) && !below.ContainsNode(x)) + { + constant = Number.Integer.One / below; + top = above; + } + foreach (var factor in Mulf.LinearChildren(top)) + { + if (!factor.ContainsNode(x)) + { + constant = constant * factor; + continue; + } + // A real function: a fractional power of one is not, where its base is + // negative, and its even power is then not the square of a modulus. + if (factor is not Powf(var function, Number.Integer degree) || !degree.EInteger.IsEven || degree.EInteger.Sign <= 0 + || TreeAnalyzer.TryGetPolynomial(function, x, out _) || function is Powf(_, not Number.Integer)) + return node; + var product = exponent.ERational.Multiply(ERational.FromEInteger(degree.EInteger)).ToLowestTerms(); + if (!product.Denominator.Equals(EInteger.One)) + return node; + functions.Add((function, product.Numerator)); + } + if (functions.Count == 0) + return node; + Entity outside = constant == Number.Integer.One ? Number.Integer.One : MathS.Pow(constant, exponent); + foreach (var (function, power) in functions) + { + // |f|^n is f^n for an even n and sgn(f) f^n for an odd one. + if (!power.IsEven) + signs = signs * MathS.Signum(function); + outside = outside * MathS.Pow(function, Number.Integer.Create(power)); + } + return outside; + }); + if (rewritten == expr) + return null; + if (Integration.ComputeAsAQuestionOfItsOwn(rewritten, x, integrateByParts) is not { } result) + return null; + var answer = signs == Number.Integer.One ? result : signs * result; + return answer.Nodes.Any(node => node == MathS.NaN) ? null : answer; + } + /// /// A denominator factor that is a sum or difference of two square roots of /// polynomials, multiplied above and below by its conjugate: diff --git a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs index 49fad0937..da2ce7c44 100644 --- a/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs +++ b/Sources/AngouriMath/Functions/Continuous/Integration/Integration.Definition.cs @@ -513,6 +513,9 @@ private static Entity Normalized(Entity expr, Entity.Variable x) => // A square root of a polynomial with a repeated factor, the factor taken out of // the root with its sign: `sqrt((x - 3)^2 (x + 1))` is `sgn(x - 3) (x - 3) sqrt(x + 1)`. if ((answer = IndefiniteIntegralSolver.SolveByTakingASquareFactorOutOfARoot(expr, x, integrateByParts)) is { }) return answer; + // A fractional power of a constant times an even power of a function, the function + // taken out with its sign: `(a sin(x)^2)^(5/2)` is `a^(5/2) sgn(sin(x)) sin(x)^5`. + if ((answer = IndefiniteIntegralSolver.SolveByTakingAFunctionOutOfAPowerOfItsEvenPower(expr, x, integrateByParts)) is { }) return answer; // A sum of two roots below the bar, multiplied above and below by its conjugate: // `1/(sqrt(A) - sqrt(B))` is `(sqrt(A) + sqrt(B))/(A - B)`. if ((answer = IndefiniteIntegralSolver.SolveByRationalisingASumOfRoots(expr, x, integrateByParts)) is { }) return answer; diff --git a/Sources/Tests/UnitTests/Calculus/EvenPowerSignTest.cs b/Sources/Tests/UnitTests/Calculus/EvenPowerSignTest.cs new file mode 100644 index 000000000..cc4ae2913 --- /dev/null +++ b/Sources/Tests/UnitTests/Calculus/EvenPowerSignTest.cs @@ -0,0 +1,83 @@ +// +// Copyright (c) 2019-2026 Angouri. +// AngouriMath is licensed under MIT. +// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md. +// Website: https://am.angouri.org. +// + +using System; +using AngouriMath.Extensions; +using Xunit; + +namespace AngouriMath.Tests.Calculus +{ + /// + /// A fractional power of a constant times an even power of a function, the function + /// taken out of the power with its sign: (a sin(x)^2)^(5/2) is + /// a^(5/2) sgn(sin(x)) sin(x)^5, and the sign is a constant between the zeros of + /// the sine that comes out in front of the integral. + /// #718 + /// + /// + /// Rubi's x sqrt(sin(x)^2), (csc(x)^2)^(3/2) and 1/(csc(x)^2)^(7/2) + /// had no antiderivative, and (a sin(x)^2)^(5/2) one in powers of the modulus of the + /// sine. Every answer is differentiated back on both sides of a zero of the function, and + /// the constant is given a negative value as well as a positive one: an even power is not + /// negative, so (c q)^p = c^p q^p for the principal powers whatever c is. + /// + [Trait("Area", "Calculus")] + public sealed class EvenPowerSignTest + { + /// On both sides of the sine's zero at pi, and of the cosine's at pi/2 and 3pi/2. + private static readonly double[] AroundTheZeros = { 0.5, 1.2, 2.0, 3.5, 5.0, 6.5 }; + + private static void DifferentiatesBack(string integrand, double[] points, double constant) + { + var integral = integrand.ToEntity().Integrate("x"); + Assert.DoesNotContain("integral(", integral.Stringize()); + Assert.DoesNotContain("NaN", integral.Stringize()); + Assert.DoesNotContain("abs(", integral.Stringize()); + + var derivative = integral.Substitute("C", 0).Substitute("a", constant).Differentiate("x"); + var original = integrand.ToEntity().Substitute("a", constant); + foreach (var at in points) + { + var got = derivative.Substitute("x", at).EvalNumerical(); + var want = original.Substitute("x", at).EvalNumerical(); + var difference = Math.Abs((double)(got - want).RealPart) + + Math.Abs((double)(got - want).ImaginaryPart); + var scale = Math.Max(1.0, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart)); + Assert.True(difference / scale < 1e-8, + $"d/dx of the antiderivative of {integrand} at a = {constant} is {got} at x = {at}, " + + $"where the integrand is {want}"); + } + } + + [Theory] + [InlineData("(a*sin(x)^2)^(5/2)")] + [InlineData("(a*sin(x)^2)^(3/2)/a")] + [InlineData("x*sqrt(a*sin(x)^2)")] + [InlineData("(a*csc(x)^2)^(3/2)")] + [InlineData("1/(a*csc(x)^2)^(7/2)")] + [InlineData("1/sqrt(a*cot(x)^2)")] + [InlineData("(a*cos(x)^4)^(3/4)")] + public void TheFunctionComesOutOfThePowerWithItsSign(string integrand) + { + DifferentiatesBack(integrand, AroundTheZeros, 2); + DifferentiatesBack(integrand, AroundTheZeros, -3); + Assert.Contains("sgn(", integrand.ToEntity().Integrate("x").Stringize()); + } + + /// An even product of the exponents has no sign to keep. + [Theory] + [InlineData("(a*sin(x)^4)^(1/2)")] + [InlineData("1/(a*cos(x)^4)^(1/2)")] + [InlineData("(a*cos(x)^4)^(3/2)")] + public void AnEvenWholePowerNeedsNoSign(string integrand) + { + DifferentiatesBack(integrand, AroundTheZeros, 2); + DifferentiatesBack(integrand, AroundTheZeros, -3); + Assert.DoesNotContain("sgn(", integrand.ToEntity().Integrate("x").Stringize()); + } + } +}