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
14 changes: 14 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,20 @@ is written so, after the substitutions, and the factor goes in front of the answ
| `"(1/(1 - x^2))^(3/2)".ToEntity().Integrate("x")` | `integral(...)` | `sgn(1 - x^2) x/sqrt(1 - x^2)` |
| `"(c/(a + b*x^2))^(3/2)".ToEntity().Integrate("x")` | `integral(...)` | `(c/(a + b x^2))^(3/2) (a + b x^2)^(3/2) x/(a sqrt(a + b x^2))` |

### A sum that is the derivative of a product of powers is read as one

**Answers where there were none.** `m x^(m - 1) (a + b x^n)^p + b n p x^(m + n - 1) (a + b x^n)^(p - 1)` is
the derivative of `x^m (a + b x^n)^p`, written out as a sum, and was declined: the rule that reads a
derivative of a product of powers reads the product beside one bracket, and split into its terms neither is
elementary. A sum of products of powers of the same bases is written as their common product, each base
to the least of its exponents, times the bracket of what is left, and asked so. Rubi's 1.1.3.2
([#718](https://github.com/asc-community/AngouriMath/issues/718)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"m*x^(m-1)*(a+b*x^n)^p + b*n*p*x^(m+n-1)*(a+b*x^n)^(p-1)".ToEntity().Integrate("x")` | `integral(...)` | `x^m (a + b x^n)^p` |
| `"-1/2*b*n*x^(-1+m+n)/(a+b*x^n)^(3/2)+m*x^(-1+m)/sqrt(a+b*x^n)".ToEntity().Integrate("x")` | `integral(...)` | `x^m/sqrt(a + b x^n)` |

### A whole power of a quotient with a symbol in it is integrated as the quotient of the powers

**Answers where there were none, and a slowdown since 2.5.0 undone.** `(c/(a + c x^2))^2` was
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12827,6 +12827,83 @@ static bool IsRationalInBoth(Entity e, Entity.Variable x, Entity.Variable L)
/// <summary>The largest power of the logarithm the tower ansatz reads or tries.</summary>
private const int MaximumTowerDegree = 4;

/// <summary>
/// <paramref name="sum"/>, every term of which is a constant times powers of the same
/// bases with <paramref name="x"/> in them, as the product of those bases to the least of
/// each one's exponents times the sum of what is left. Null where a base is not in every
/// term, where no exponent of one is the least, or where the least are all whole, which
/// the rational rules read.
/// </summary>
private static Entity? WithTheCommonPowersOut(Entity sum, Entity.Variable x)
{
var terms = new List<(Entity Constant, Dictionary<Entity, Entity> Powers)>();
foreach (var term in Sumf.LinearChildren(sum))
{
Entity constant = Number.Integer.One;
var powers = new Dictionary<Entity, Entity>();
foreach (var (factor, underneath) in FactorsOfTheIntegrand(term))
{
if (!factor.ContainsNode(x))
{
constant = underneath ? constant / factor : constant * factor;
continue;
}
var (@base, exponent) = factor is Powf(var b, var e) && !e.ContainsNode(x) ? (b, e) : (factor, (Entity)Number.Integer.One);
if (@base is Number || !@base.ContainsNode(x))
return null;
if (underneath)
exponent = -exponent;
powers[@base] = powers.TryGetValue(@base, out var before) ? (before + exponent).InnerSimplified : exponent.InnerSimplified;
}
terms.Add((constant, powers));
}
if (terms.Count < 2)
return null;
var bases = terms[0].Powers.Keys.ToList();
if (bases.Count == 0 || terms.Any(term => term.Powers.Count != bases.Count || bases.Any(@base => !term.Powers.ContainsKey(@base))))
return null;
// For each base the least of its exponents, against which every other is more by a
// whole number or by a sum of symbols with no sign in front: `x^(m - 1)` beside
// `x^(m + n - 1)` leaves `x^n` in the bracket, as the derivative of `a + b x^n` does.
// Simplified, not inner-simplified: `-1 + m + n - (-1 + m)` is left as written by the
// latter, and is `n`.
static Entity Difference(Entity exponent, Entity from)
=> Functions.PartialFractions.Bare((exponent - from).Simplify());
static bool More(Entity difference)
=> difference is Number.Integer { EInteger.Sign: >= 0 }
|| difference is not Number && difference is not Mulf(Number.Real { IsNegative: true }, _) && difference.Vars.Any();
var least = new Dictionary<Entity, Entity>();
foreach (var @base in bases)
{
var exponents = terms.Select(term => term.Powers[@base]).ToList();
Entity? lowest = null;
foreach (var candidate in exponents)
if (exponents.All(other => Difference(other, candidate) is var difference && (difference == Number.Integer.Zero || More(difference))))
{
lowest = candidate;
break;
}
if (lowest is null)
return null;
least[@base] = lowest;
}
if (least.Values.All(exponent => exponent is Number.Integer))
return null;
Entity bracket = Number.Integer.Zero;
foreach (var (constant, powers) in terms)
{
Entity rest = constant;
foreach (var @base in bases)
if (Difference(powers[@base], least[@base]) is var raised && raised != Number.Integer.Zero)
rest = rest * (raised == Number.Integer.One ? @base : MathS.Pow(@base, raised));
bracket = bracket == Number.Integer.Zero ? rest : bracket + rest;
}
Entity common = Number.Integer.One;
foreach (var @base in bases)
common = common * MathS.Pow(@base, least[@base]);
return common * bracket;
}

/// <summary>
/// A product of powers times a sum that is the derivative of the product with some of
/// the powers raised by one: <c>e^x x^2 ln(x)^2 (3 + (3 + x) ln(x))</c> is
Expand Down Expand Up @@ -12859,6 +12936,13 @@ static bool IsRationalInBoth(Entity e, Entity.Variable x, Entity.Variable L)
{
if (!Integration.AnsweringTheQuestionAskedOrOneBelow)
return null;
// A sum of products of powers of the same bases is their common product times a
// bracket, which is the shape read below: `m x^(m - 1) (a + b x^n)^p + b n p x^(m + n - 1)
// (a + b x^n)^(p - 1)` is `x^(m - 1) (a + b x^n)^(p - 1) (m (a + b x^n) + b n p x^n)`,
// the derivative of `x^m (a + b x^n)^p`. As a sum it was split, and neither term is
// elementary.
if (expr is Sumf or Minusf)
return WithTheCommonPowersOut(expr, x) is { } factored ? SolveAsTheDerivativeOfAProductOfPowers(factored, x) : null;
Entity constant = Number.Integer.One;
Entity? bracket = null;
var raisable = new List<(Entity Base, Entity Exponent)>();
Expand Down
11 changes: 11 additions & 0 deletions Sources/Tests/UnitTests/Calculus/ProductOfPowersDerivativeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,17 @@ public void TwoSymbolicPowersOfPolynomials()
public void WithoutASumOrWithXRaisedFromNothing(string integrand)
=> DifferentiatesBackWithParametersPinned(integrand, Points);

/// <summary>
/// The derivative written out as a sum, the common powers in each term: they come out in
/// front and the rest is the bracket, where split into its terms neither is elementary.
/// Rubi's 1.1.3.2.
/// </summary>
[Theory]
[InlineData("-1/2*b*n*x^(-1+m+n)/(a+b*x^n)^(3/2)+m*x^(-1+m)/sqrt(a+b*x^n)")]
[InlineData("m*x^(m-1)*(a+b*x^n)^p + b*n*p*x^(m+n-1)*(a+b*x^n)^(p-1)")]
public void ASumWithTheCommonPowersInEachTerm(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()
Expand Down
Loading