Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -1983,6 +1983,21 @@ and in no release -- and is `sgn(u) u/(u^2 - 1)`
| `"1/sqrt(1+csch(x)^2)".Integrate("x")` | left unevaluated | `-sgn(tanh(x)) ln(tanh(x)^2 - 1)/2`, in exponentials |
| `"x^4/sech(2*ln(c*x))^(1/2)".Integrate("x")` | left unevaluated | `sgn(x) (1 + c^4 x^4)^(3/2)/(6 sqrt(2) c^5)` up to the form, `provided c^4 > 0` |

### A product of powers is read as its own raised product's derivative without a sum beside it

**Improvement, not silent.** An integrand that is a constant times the derivative of its powers
with some of them raised by one is answered by that product. The rule wanted a sum beside the powers
and a power of `x` among them, and read the sum's quotient through `f'/f`, which with two symbolic
powers of polynomials ran past a minute. Where a power is not a whole one it now reads a product
with no sum, and `x` raised from no power at all, and the sum as a polynomial
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"1/(x^2*sqrt(a*x+b*x^4))".Integrate("x")` | left unevaluated | `-2 sqrt(a x + b x^4)/(3 a x^2)` |
| `"(a+b*x^n+c*x^(2*n))^p*(a+b*(1+n+n*p)*x^n+c*(1+2*n*(1+p))*x^(2*n))".Integrate("x")`, Rubi's 1.2.3.5 | no answer in 30 s | `x (a + b x^n + c x^(2 n))^(p + 1)` |
| Rubi 1.3.2's `x (a + b x + c x^2)^m (d + e x + f x^2 + g x^3)^n` times the sum its derivative leaves | no answer in 90 s | `x^2 (a + b x + c x^2)^(m + 1) (d + e x + f x^2 + g x^3)^(n + 1)` |

### A root of a rational function of `coth(x)` is integrated by the hyperbolic half-angle

`coth(x)^3/sqrt(a + b coth(x)^2)` was a search past the budget. The rational-in-`tanh` route
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9055,6 +9055,11 @@ static bool IsRationalInBoth(Entity e, Entity.Variable x, Entity.Variable L)
/// ansatz wants whole powers of its monomials, and splitting the sum loses it, since
/// <c>F^(c(a + bx)) x^m ln(dx)^n</c> on its own is not elementary.
/// </para>
/// <para>
/// Where a power is not a whole one, the sum may be missing -- the product of powers is
/// then a constant times its own raised product's derivative -- and x may be raised from
/// nothing, a power of it the integrand does not have.
/// </para>
/// https://github.com/asc-community/AngouriMath/issues/718
/// </remarks>
internal static Entity? SolveAsTheDerivativeOfAProductOfPowers(Entity expr, Entity.Variable x)
Expand Down Expand Up @@ -9095,34 +9100,68 @@ static bool IsRationalInBoth(Entity e, Entity.Variable x, Entity.Variable L)
}
raisable.Add((factor, underneath ? Number.Integer.MinusOne : Number.Integer.One));
}
if (bracket is null || raisable.Count == 0 || raisable.Count > 4)
if (raisable.Count == 0 || raisable.Count > 4)
return null;
// Where a power is not a whole one -- a root, or a symbolic exponent -- two more
// candidates, each a shape nothing else reads. With no sum beside two or more powers,
// the product is the derivative's up to a constant: `1/(x^2 sqrt(a x + b x^4))` is
// `(-2/(3 a) sqrt(a x + b x^4)/x^2)'`, the root raised and the power of x kept; a
// power on its own is the power rule's, or the binomial's. And x raised from nothing
// where it is not among the powers, in front of them: Rubi's
// `(a + b x^n + c x^(2 n))^p (a + b (1 + n + n p) x^n + c (1 + 2 n (1 + p)) x^(2 n))`
// is `(x (a + b x^n + c x^(2 n))^(p + 1))'`. Whole powers only are a rational
// function's, or a polynomial's beside a function, and the rules for those answer them.
if (raisable.Any(pair => pair.Exponent is not Number.Integer))
{
if (raisable.Count >= 2)
bracket ??= Number.Integer.One;
if (!raisable.Any(pair => pair.Base == x))
raisable.Insert(0, (x, Number.Integer.Zero));
}
if (bracket is null)
return null;
for (var subset = 1; subset < 1 << raisable.Count; subset++)
{
Entity candidate = kept;
Entity productOfRaisedBases = Number.Integer.One;
Entity logarithmicDerivative = Number.Integer.Zero;
for (var i = 0; i < raisable.Count; i++)
if ((subset & (1 << i)) != 0)
productOfRaisedBases *= raisable[i].Base;
// The bracket the candidate's derivative has, against the integrand's: the
// candidate is the integrand's powers with the raised bases in besides, so
// G' is (integrand without its bracket) (product of raised bases) (sum of the
// logarithmic derivatives), and the constant is the quotient of the brackets.
// Written as a sum of products, each raised base's logarithmic derivative times
// the other raised bases, so that it reads as a polynomial wherever the bases do:
// with `f'/f` left in, it read as nothing, and the constant fell to simplifying a
// quotient of the two brackets -- past a minute for Rubi's
// `x (a + b x + c x^2)^m (d + e x + f x^2 + g x^3)^n (...)`, 0.3 s read off one
// monomial.
Entity bracketOfTheDerivative = Number.Integer.Zero;
foreach (var derivative in logarithmicDerivativesKept)
logarithmicDerivative += derivative;
bracketOfTheDerivative += derivative * productOfRaisedBases;
for (var i = 0; i < raisable.Count; i++)
{
var (@base, exponent) = raisable[i];
var raised = (subset & (1 << i)) != 0;
var newExponent = raised ? (exponent + 1).InnerSimplified : exponent;
if (raised)
productOfRaisedBases *= @base;
if (newExponent.Evaled is Number.Complex { IsZero: true })
continue; // a power raised to nothing: not a factor of the candidate
candidate *= newExponent == Number.Integer.One ? @base : MathS.Pow(@base, newExponent);
logarithmicDerivative += newExponent * @base.Differentiate(x) / @base;
if (!raised)
{
bracketOfTheDerivative += newExponent * @base.Differentiate(x) * productOfRaisedBases / @base;
continue;
}
Entity theOtherRaisedBases = Number.Integer.One;
for (var j = 0; j < raisable.Count; j++)
if (j != i && (subset & (1 << j)) != 0)
theOtherRaisedBases *= raisable[j].Base;
bracketOfTheDerivative += newExponent * @base.Differentiate(x) * theOtherRaisedBases;
}
if (candidate == kept)
continue;
// The bracket the candidate's derivative has, against the integrand's: the
// candidate is the integrand's powers with the raised bases in besides, so
// G' is (integrand without its bracket) (product of raised bases) (sum of the
// logarithmic derivatives), and the constant is the quotient of the brackets.
var bracketOfTheDerivative = Functions.PartialFractions.Bare((productOfRaisedBases * logarithmicDerivative).InnerSimplified);
bracketOfTheDerivative = Functions.PartialFractions.Bare(bracketOfTheDerivative.InnerSimplified);
if (!AreProportionalAtSampledPoints(bracket, bracketOfTheDerivative, x))
continue;
// The constant is the quotient of one monomial's coefficients, the two
Expand Down
25 changes: 25 additions & 0 deletions Sources/Tests/UnitTests/Calculus/ProductOfPowersDerivativeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,5 +104,30 @@ public void TheConstantIsRead()
[Fact]
public void WhatIsNotADerivativeIsDeclined()
=> Assert.Contains("integral(", "x^m*ln(x)^n*(1 + ln(x))".ToEntity().Integrate("x").Stringize());

/// <summary>
/// Rubi's 1.3.2, two symbolic powers of a quadratic and a cubic beside a power of x:
/// the sum is read as a polynomial and the constant off one monomial. It was a timeout.
/// </summary>
[Fact]
public void TwoSymbolicPowersOfPolynomials()
=> DifferentiatesBackWithParametersPinned(
"x*(a+b*x+c*x^2)^m*(d+h*x+f*x^2+g*x^3)^n*(2*a*d+(3*b*d+3*a*h+b*d*m+a*h*n)*x+(4*c*d+4*b*h+4*a*f+2*c*d*m+b*h*m+b*h*n+2*a*f*n)*x^2+(5*c*h+5*b*f+5*a*g+2*c*h*m+b*f*m+c*h*n+2*b*f*n+3*a*g*n)*x^3+(6*c*f+6*b*g+2*c*f*m+b*g*m+2*c*f*n+3*b*g*n)*x^4+c*g*(7+2*m+3*n)*x^5)",
Points);

/// <summary>
/// Where a power is not a whole one, a product with no sum beside it, Rubi's 1.1.4.2, and
/// x raised from a power the integrand does not have, Rubi's 1.2.3.5.
/// </summary>
[Theory]
[InlineData("1/(x^2*sqrt(a*x+b*x^4))")]
[InlineData("(a+b*x^n+c*x^(2*n))^p*(a+b*(1+n+n*p)*x^n+c*(1+2*n*(1+p))*x^(2*n))")]
public void WithoutASumOrWithXRaisedFromNothing(string integrand)
=> DifferentiatesBackWithParametersPinned(integrand, Points);

/// <summary>A product of powers that is no such derivative is still declined: <c>sqrt(1 + x^3)</c> is elliptic.</summary>
[Fact]
public void APowerThatIsNoDerivativeIsDeclined()
=> Assert.Contains("integral(", "sqrt(1 + x^3)".ToEntity().Integrate("x").Stringize());
}
}
Loading