Skip to content

Commit 61de03d

Browse files
A power of a multiple of a quadratic's derivative beside a power of the quadratic is a binomial (#1691)
Rubi's 1.2.1.2 (b d + 2 c d x)^m (a + b x + c x^2)^p, with a power that is not whole, was declined or ran out of time: no substitution the search tried saw that the linear is a multiple of the quadratic's derivative. Under t = b d + 2 c d x the quadratic is (t^2/d^2 - (b^2 - 4 a c))/(4 c), exactly, since the substitution is affine, and the integrand is a binomial in t. The quadratic is written in t from its coefficients rather than by substituting, which would leave a linear term zero as a value and not as written; and each constant of the binomial that is a compound of the coefficients is named by a symbol of its own and written back into the answer. The rules answer t^(-7) sqrt(c t^2 + k) in 63 ms, and with a - b^2/(4 c) for k they ran past twenty seconds. A quadratic with no linear term is a binomial already and is left alone, else the rule would ask its own question with t scaled. Part of #718. Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent f256774 commit 61de03d

4 files changed

Lines changed: 156 additions & 0 deletions

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,21 @@ taken apart into pieces each closed the same way
491491
| `"(2 + x)/((2 + 4*x - 3*x^2)*(1 + 3*x + 2*x^2)^(3/2))".ToEntity().Integrate("x")` | `integral(...)` | an antiderivative |
492492
| `"1/((x^2 + 1)*sqrt(x^2 + x + 1))".ToEntity().Integrate("x")` | `integral(...)` | a logarithm and an arctangent |
493493

494+
### A power of a multiple of a quadratic's derivative beside a power of the quadratic is a binomial
495+
496+
**Improvement, not silent.** `(b d + 2 c d x)^m (a + b x + c x^2)^p`, with a power that is not whole,
497+
was declined or ran out of time. Under `t = b d + 2 c d x` the quadratic is
498+
`(t^2/d^2 - (b^2 - 4 a c))/(4 c)`, so the integrand is a binomial in `t`. That binomial is asked with
499+
its constant term named by a symbol of its own, since with `a - b^2/(4 c)` written in it the rules for
500+
a binomial ran out of time too. Rubi's 1.2.1.2
501+
([#718](https://github.com/asc-community/AngouriMath/issues/718)).
502+
503+
| Input | Was (2.5.0) | Now |
504+
|---|---|---|
505+
| `"(a+b*x+c*x^2)^(3/2)/(b*d+2*c*d*x)^3".Integrate("x")` | `integral(...)` | the antiderivative |
506+
| `"(a+b*x+c*x^2)^(1/2)/(b*d+2*c*d*x)^7".Integrate("x")` | `integral(...)` | the antiderivative |
507+
| `"(b*d+2*c*d*x)^(5/2)/(a+b*x+c*x^2)^3".Integrate("x")` | `integral(...)` | the antiderivative |
508+
494509
### A polynomial over a power of a quadratic beside the root of another is integrated
495510

496511
`P/(A^k sqrt(B))`, `A` and `B` two different quadratics and `k` at least two, was left

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

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14072,6 +14072,88 @@ bool MayBeARootOfASquare(Entity node, Entity.Variable x)
1407214072
return assumed == Entity.Boolean.True ? answer : answer.Provided(assumed);
1407314073
}
1407414074

14075+
/// <summary>
14076+
/// A power of a linear that is a constant multiple of a quadratic's derivative, beside a
14077+
/// power of the quadratic, under the linear as the variable: with <c>t = λ (b + 2 c x)</c>
14078+
/// the quadratic <c>a + b x + c x^2</c> is <c>(t^2/λ^2 - Δ)/(4 c)</c>, with
14079+
/// <c>Δ = b^2 - 4 a c</c>, so <c>t^m Q^p</c> is a binomial in <c>t</c>. Rubi's 1.2.1.2,
14080+
/// <c>(b d + 2 c d x)^m (a + b x + c x^2)^p</c>, which it substitutes the same way.
14081+
/// </summary>
14082+
/// <remarks>
14083+
/// The substitution is affine, so the integrand in <c>t</c> is the integrand exactly. The
14084+
/// quadratic is written in <c>t</c> from its coefficients rather than by substituting:
14085+
/// substituted, it keeps a linear term that is zero as a value and not as written, and the
14086+
/// rules for a binomial read <c>α + β t^2</c>. Only where a power is not whole: a rational
14087+
/// function is the partial fractions'.
14088+
/// https://github.com/asc-community/AngouriMath/issues/718
14089+
/// </remarks>
14090+
internal static Entity? SolveByTheDerivativeOfAQuadraticAsTheVariable(Entity expr, Entity.Variable x, bool integrateByParts)
14091+
{
14092+
if (!Integration.AnsweringTheQuestionAskedOrOneBelow)
14093+
return null;
14094+
Entity constant = Number.Integer.One;
14095+
(Entity Base, Entity Exponent)? linear = null, quadratic = null;
14096+
foreach (var (factor, underneath) in FactorsOfTheIntegrand(expr))
14097+
{
14098+
if (!factor.ContainsNode(x))
14099+
{
14100+
constant = underneath ? constant / factor : constant * factor;
14101+
continue;
14102+
}
14103+
var (@base, exponent) = factor is Powf(var b, var e) && !e.ContainsNode(x) ? (b, e) : (factor, (Entity)Number.Integer.One);
14104+
if (underneath)
14105+
exponent = (-exponent).InnerSimplified;
14106+
if (!TreeAnalyzer.TryGetPolynomial(@base, x, out var read) || read.Count == 0 || read.Keys.Any(k => k.Sign < 0)
14107+
|| read.Values.Any(coefficient => coefficient.ContainsNode(x)))
14108+
return null;
14109+
var degree = read.Keys.Max()!;
14110+
if (degree.Equals(EInteger.One) && linear is null)
14111+
linear = (@base, exponent);
14112+
else if (degree.Equals(EInteger.FromInt32(2)) && quadratic is null)
14113+
quadratic = (@base, exponent);
14114+
else
14115+
return null;
14116+
}
14117+
if (linear is not var (l, m) || quadratic is not var (q, p) || m is Number.Integer && p is Number.Integer)
14118+
return null;
14119+
if (!TreeAnalyzer.TryGetPolyLinear(l, x, out var l1, out var l0)
14120+
|| !TreeAnalyzer.TryGetPolyQuadratic(q, x, out var c2, out var c1, out var c0)
14121+
|| VanishesIdentically(c2) || VanishesIdentically(l1)
14122+
// With no linear term the integrand is a binomial in x already, and taken as one in
14123+
// t it would be this question again with t scaled.
14124+
|| VanishesIdentically(c1)
14125+
// The linear is a multiple of 2 c x + b where its root is the vertex's.
14126+
|| !VanishesIdentically(2 * c2 * l0 - c1 * l1))
14127+
return null;
14128+
var lambda = Reduced(l1 / (2 * c2));
14129+
// Q = (t^2/λ^2 - Δ)/(4 c) = α + β t^2, each constant named by a symbol of its own where
14130+
// it is a compound of the coefficients, and written back into the answer: the rules for
14131+
// a binomial answer `t^(-7) sqrt(c t^2 + k)` in milliseconds, and with
14132+
// `a - b^2/(4 c)` for `k` they ran out of time.
14133+
var alpha = Reduced(-(c1 * c1 - 4 * c2 * c0) / (4 * c2));
14134+
var beta = Reduced(1 / (4 * c2 * lambda * lambda));
14135+
var t = Variable.CreateUnique(expr, "t_deriv");
14136+
var named = new List<(Variable Name, Entity Value)>();
14137+
Entity Named(Entity value)
14138+
{
14139+
if (value is Number or Variable || value is Mulf(Number, Variable) || value is Powf(Variable, Number))
14140+
return value;
14141+
var name = Variable.CreateUnique(expr + t + named.Aggregate((Entity)Number.Integer.Zero, (sum, pair) => sum + pair.Name), "k_named");
14142+
named.Add((name, value));
14143+
return name;
14144+
}
14145+
var inT = Reduced(constant / l1) * MathS.Pow(t, m) * MathS.Pow(Named(alpha) + Named(beta) * MathS.Sqr(t), p);
14146+
if (Integration.ComputeAsAQuestionOfItsOwn(inT, t, integrateByParts) is not { } answer
14147+
|| answer.Nodes.Any(node => node == MathS.NaN))
14148+
return null;
14149+
foreach (var (name, value) in named)
14150+
answer = answer.Substitute(name, value);
14151+
return answer.Substitute(t, l);
14152+
14153+
static Entity Reduced(Entity constant)
14154+
=> constant.Vars.Any() ? Functions.PartialFractions.InLowestTermsOverTheSymbols(constant) : constant.InnerSimplified;
14155+
}
14156+
1407514157
/// <summary>
1407614158
/// A whole power of a product in which the variable stands beside a constant, written
1407714159
/// as the product of the powers: <c>(c x)^2</c> is <c>c^2 x^2</c>, exact for a whole

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,9 @@ private static Entity Normalized(Entity expr, Entity.Variable x) =>
662662
if ((answer = IndefiniteIntegralSolver.SolveAReciprocalOfAnInverseTrigonometricFunction(expr, x, integrateByParts)) is { }) return answer;
663663
// A fractional power of a perfect square is the power of the modulus, sgn(P) P^(2r).
664664
if ((answer = IndefiniteIntegralSolver.SolveByTakingARootOfAPerfectSquare(expr, x, integrateByParts)) is { }) return answer;
665+
// A power of a multiple of a quadratic's derivative beside a power of the quadratic is
666+
// a binomial in the derivative: `(b d + 2 c d x)^m (a + b x + c x^2)^p`.
667+
if ((answer = IndefiniteIntegralSolver.SolveByTheDerivativeOfAQuadraticAsTheVariable(expr, x, integrateByParts)) is { }) return answer;
665668
// x^(n - 1) g(x^n) with a symbolic n is g(u)/n under u = x^n.
666669
if ((answer = IndefiniteIntegralSolver.SolveByAPowerOfTheVariableTimesAFunctionOfItsPower(expr, x, integrateByParts)) is { }) return answer;
667670
// A whole power of a product of a constant and the variable, as the product of
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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 multiple of a quadratic's derivative beside a power of the quadratic, Rubi's
16+
/// 1.2.1.2 <c>(b d + 2 c d x)^m (a + b x + c x^2)^p</c>: under <c>t = b d + 2 c d x</c> the
17+
/// quadratic is <c>(t^2/d^2 - (b^2 - 4 a c))/(4 c)</c>, and the integrand a binomial in <c>t</c>.
18+
/// <a href="https://github.com/asc-community/AngouriMath/issues/718">#718</a>
19+
/// </summary>
20+
/// <remarks>
21+
/// Each was declined, or ran past the corpus's budget; the binomial with a compound constant
22+
/// term ran out of time too, and is asked with the constant named by a symbol of its own.
23+
/// Checked by differentiating back with the symbols pinned, at points on both sides of the
24+
/// derivative's root where the integrand is real.
25+
/// </remarks>
26+
[Trait("Area", "Calculus")]
27+
public sealed class DerivativeOfAQuadraticAsTheVariableTest
28+
{
29+
[Theory]
30+
[InlineData("(a + b*x + c*x^2)^(3/2)/(b*d + 2*c*d*x)^3")]
31+
[InlineData("(a + b*x + c*x^2)^(1/2)/(b*d + 2*c*d*x)^7")]
32+
[InlineData("(a + b*x + c*x^2)^(5/2)/(b*d + 2*c*d*x)^5")]
33+
[InlineData("(b*d + 2*c*d*x)^(5/2)/(a + b*x + c*x^2)^3")]
34+
public void IsABinomialInTheDerivative(string integrand)
35+
{
36+
var integral = integrand.ToEntity().Integrate("x").Substitute("C", 0);
37+
Assert.DoesNotContain("integral(", integral.Stringize());
38+
Entity Pin(Entity e) => e.Substitute("a", 2.3).Substitute("b", 1.1).Substitute("c", 0.7).Substitute("d", 1.9);
39+
var derivative = Pin(integral).Differentiate("x");
40+
var original = Pin(integrand.ToEntity());
41+
var compared = 0;
42+
foreach (var at in new[] { -3.1, -1.4, 0.3, 0.9, 1.7 })
43+
{
44+
var want = original.Substitute("x", at).EvalNumerical();
45+
if (want.IsNaN || Math.Abs((double)want.ImaginaryPart) > 1e-12 * Math.Max(1, Math.Abs((double)want.RealPart)))
46+
continue;
47+
var got = derivative.Substitute("x", at).EvalNumerical();
48+
compared++;
49+
var difference = Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart);
50+
Assert.True(difference < 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart)),
51+
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
52+
}
53+
Assert.True(compared >= 3, $"only {compared} points where {integrand} is real");
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)