Skip to content

Commit acd4702

Browse files
Take a root of a perfect square with a leading coefficient of no known sign (#1884)
* Take a root of a perfect square with a leading coefficient of no known sign (a (w + h)^2)^r is a^r |w + h|^(2r) for every nonzero a where w + h is real, since its square is positive: a negative number was read so already, and a symbol of no known sign is now. And a linear of the integrand that is a multiple of the square's root is taken as the root, so that its power and the square's gather into one. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura * Record the answers a perfect square's root gives Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 033953d commit acd4702

3 files changed

Lines changed: 106 additions & 3 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,26 @@ quadratic with numbers in it, although beside the root each piece is a shape the
541541
| `"(2 + 5*x + x^2)*sqrt(3 + 2*x + 5*x^2)/(1 + 4*x - 7*x^2)^3".ToEntity().Integrate("x")` | `integral(...)` | 5,242 characters |
542542
| `"(a + b*x + c*x^2)^(1/2)/(d + e*x + f*x^2)^2".ToEntity().Integrate("x")` | `integral(...)` | 50,758 characters, six symbols |
543543

544+
### A power of a perfect square with a symbolic leading coefficient is a power of its root
545+
546+
**Answers where there were none.** `sqrt(c d^2 + 2 c d k x + c k^2 x^2)` was declined: the square root of
547+
`c (d + k x)^2` was taken as `sqrt(c) |d + k x|` only for a leading coefficient whose sign was known. It
548+
holds for every nonzero one, since `(d + k x)^2` is positive, and is taken so now. Beside a power of the
549+
square's own linear, that linear is taken as the root, so the two powers gather into one. The reciprocal
550+
root, which 2.5.0 answered in 411 characters and the unreleased master declined, is 61 characters now
551+
([#718](https://github.com/asc-community/AngouriMath/issues/718)).
552+
553+
| Input | Was (2.5.0) | Now |
554+
|---|---|---|
555+
| `"sqrt(c*d^2 + 2*c*d*k*x + c*k^2*x^2)".ToEntity().Integrate("x")` | `integral(...)` | 62 characters |
556+
| `"(c*d^2 + 2*c*d*k*x + c*k^2*x^2)^(3/2)".ToEntity().Integrate("x")` | `integral(...)` | 62 characters |
557+
| `"1/sqrt(c*d^2 + 2*c*d*k*x + c*k^2*x^2)".ToEntity().Integrate("x")` | 411 characters | 61 characters |
558+
| `"1/(c*d^2 + 2*c*d*k*x + c*k^2*x^2)^(5/2)".ToEntity().Integrate("x")` | `integral(...)` | 74 characters |
559+
| `"(d + k*x)^(-1 - 2*p)*(c*d^2 + 2*c*d*k*x + c*k^2*x^2)^p".ToEntity().Integrate("x")` | `integral(...)` | 103 characters |
560+
| `"1/((d + k*x)*sqrt(b^2/(4*c) + b*x + c*x^2))".ToEntity().Integrate("x")` | `integral(...)` | 1,277 characters |
561+
| `"sqrt(a + a*sech(c + d*x))".ToEntity().Integrate("x")` | `integral(...)` | 274 characters |
562+
| `"asinh(sqrt(b*x^2 - 1))^3/sqrt(b*x^2 - 1)".ToEntity().Integrate("x")` | `integral(...)` | 166 characters |
563+
544564
### A square of a symbolic quadratic beside two linears is answered within the budget
545565

546566
**Answers where there were none in time.** `1/((d + k x)(f + g x)(a + b x + c x^2)^2)` was declined by

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

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21417,12 +21417,17 @@ bool MayBeARootOfASquare(Entity node, Entity.Variable x)
2141721417
// with the answer. The other two coefficients are real or symbolic, not a
2141821418
// number off the line, so that `(x + h)^2` is not negative.
2141921419
// A whole power needs no sign of anything: (a (w + h)^2)^n is a^n (w + h)^(2n).
21420+
// And one of no known sign: for a real w + h, (w + h)^2 is positive, so
21421+
// Log(a (w + h)^2) is Log(a) + 2 ln|w + h| for every nonzero a, and
21422+
// (a (w + h)^2)^r is a^r |w + h|^(2r) -- `sqrt(c (d + k x)^2)` is
21423+
// `sqrt(c) |d + k x|` for a negative c as for a positive one.
2142021424
if (!whole && a.Evaled is not Number.Real { IsZero: false })
2142121425
{
21422-
if (!IsPositiveForARealParameter(a, x, out var leadingAssumed, assumed)
21423-
&& !IsPositiveForARealParameter((-a).InnerSimplified, x, out leadingAssumed, assumed))
21426+
if (a.Evaled is Number.Complex and not Number.Real)
2142421427
return node;
21425-
assumed = leadingAssumed;
21428+
if (IsPositiveForARealParameter(a, x, out var leadingAssumed, assumed)
21429+
|| IsPositiveForARealParameter((-a).InnerSimplified, x, out leadingAssumed, assumed))
21430+
assumed = leadingAssumed;
2142621431
}
2142721432
var leading = a;
2142821433
if (b.Evaled is Number.Complex and not Number.Real || c.Evaled is Number.Complex and not Number.Real
@@ -22058,6 +22063,21 @@ static Entity Exponent(Entity exponent)
2205822063
if (found is not var (square, exponent, root, leading, squareIsBelow)
2205922064
|| above.Nodes.Concat(below.Nodes).Count(node => node == square) != 1)
2206022065
return null;
22066+
// A linear of the integrand that is a multiple of the root, `d + k x` beside
22067+
// `(c d^2 + 2 c d k x + c k^2 x^2)^p`, is the root itself, with the multiple's square
22068+
// taken out of the leading coefficient: its power and the square's then gather into
22069+
// one power of one linear, where `(x + d/k)^(2p) (d + k x)^(-1 - 2p)` were two.
22070+
if (root is Sumf && TreeAnalyzer.TryGetPolyLinear(root, x, out _, out var rootShift))
22071+
foreach (var factor in Mulf.LinearChildren(above).Concat(Mulf.LinearChildren(below)))
22072+
{
22073+
var linear = factor is Powf(var @base, var power) && !power.ContainsNode(x) ? @base : factor;
22074+
if (linear == square || !TreeAnalyzer.TryGetPolyLinear(linear, x, out var slope, out var intercept)
22075+
|| slope.Evaled is Number.Complex { IsZero: true } || !VanishesIdentically(intercept - slope * rootShift))
22076+
continue;
22077+
root = linear;
22078+
leading = leading / (slope * slope);
22079+
break;
22080+
}
2206122081
var twice = (2 * exponent).InnerSimplified;
2206222082
// The root's power above the bar either way, with a negative exponent for the square
2206322083
// below it: `x (x + h)^(-2p)` is read below the top where `x/(x + h)^(2p)` was not.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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 a perfect square whose leading coefficient is a symbol of no known sign:
16+
/// <c>(c (d + k x)^2)^r</c> is <c>(c k^2)^r |x + d/k|^(2r)</c> for a negative <c>c</c> as for a
17+
/// positive one, since <c>(x + d/k)^2</c> is positive. Rubi's 1.2.1.2
18+
/// <c>sqrt(c d^2 + 2 c d e x + c e^2 x^2)</c>, written here with <c>k</c> for <c>e</c>, which is
19+
/// Euler's number.
20+
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
21+
/// </summary>
22+
[Trait("Area", "Calculus")]
23+
public sealed class APerfectSquareWithASymbolicLeadingCoefficientIntegralTest
24+
{
25+
private static void DifferentiatesBack(string integrand, params (string, double)[] pins)
26+
{
27+
var integral = integrand.ToEntity().Integrate("x");
28+
Assert.DoesNotContain("integral(", integral.Stringize());
29+
30+
// Pinned before it is differentiated: the answer carries `sgn(x + d/k)`, whose
31+
// derivative is read where its argument is a number.
32+
var pinned = integral.Substitute("C", 0);
33+
Entity original = integrand.ToEntity();
34+
foreach (var (name, value) in pins)
35+
{
36+
pinned = pinned.Substitute(name, value);
37+
original = original.Substitute(name, value);
38+
}
39+
var derivative = pinned.Differentiate("x");
40+
foreach (var at in new[] { -2.3, -0.7, 0.4, 1.9 })
41+
{
42+
var got = derivative.Substitute("x", at).EvalNumerical();
43+
var want = original.Substitute("x", at).EvalNumerical();
44+
var difference = Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart);
45+
var scale = Math.Max(1.0, Math.Abs((double)want.RealPart) + Math.Abs((double)want.ImaginaryPart));
46+
Assert.True(difference / scale < 1e-8,
47+
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
48+
}
49+
}
50+
51+
[Theory]
52+
[InlineData("sqrt(c*d^2+2*c*d*k*x+c*k^2*x^2)")]
53+
[InlineData("(c*d^2+2*c*d*k*x+c*k^2*x^2)^(3/2)")]
54+
[InlineData("1/sqrt(c*d^2+2*c*d*k*x+c*k^2*x^2)")]
55+
[InlineData("1/(c*d^2+2*c*d*k*x+c*k^2*x^2)^(5/2)")]
56+
[InlineData("(d+k*x)^(-1-2*p)*(c*d^2+2*c*d*k*x+c*k^2*x^2)^p")]
57+
public void IsAnsweredAndDifferentiatesBack(string integrand)
58+
{
59+
DifferentiatesBack(integrand, ("c", 3), ("d", 1.25), ("k", 2), ("p", 0.3));
60+
DifferentiatesBack(integrand, ("c", -2), ("d", -0.5), ("k", 1.5), ("p", -1.7));
61+
}
62+
}
63+
}

0 commit comments

Comments
 (0)