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
17 changes: 17 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,23 @@ Rubi's 4.5.3.1:640 was wrong on that region
|---|---|---|
| `"(1+sec(x))^(5/2)*sqrt(cos(x))".Integrate("x")` | `integral((1 + sec(x)) ^ (5/2) * sqrt(cos(x)), x)` — left unevaluated | an antiderivative with `sgn(sin(x))` in it, `provided cos(x) >= 0` |

### An answer that took an even root real without reading its sign says where it holds

The same step's answer is checked where the rules below read no sign of `u` and still took it real:
`SolveByCombiningRadicals` writes `sqrt(1 + 1/u^2) sqrt(1 - u^4)` as
`sqrt(u^4 - u^8 + u^2 - u^6)/u^2`, which reads `sqrt(u^4)` as `u^2`, the other root wherever `u` is
imaginary. Where the integrand holds a second even root, as it must to be real where the radicand
is negative, the answer is differentiated and compared with the integrand where the integrand is
real. Where it is not the antiderivative there, it now carries `provided t >= 0` as well.
`sqrt(cos(x))/sqrt(a + a sec(x))` and six of its kin in Rubi's 4.5.1.2 and 4.5.3.1 were answered
negated wherever the cosine is negative; the half-angle rule above answers those seven first now,
and this is the same answer reached through the even root
([#1581](https://github.com/asc-community/AngouriMath/issues/1581)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"sqrt(x)/(sqrt(1/x + 1)*sqrt(1 - x^2))".Integrate("x")` | `integral(...)` | an antiderivative in `sqrt(x)`, `provided x >= 0` |

### The inverse hyperbolic tangent's answer is given on its domain, and says so

`1/((1 - a^2 x^2)^(5/2) artanh(a x))` was left unevaluated, although the substitution `a x = tanh(u)`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1869,6 +1869,19 @@ internal static bool DerivativeHoldsAtSampledPoints(Entity antiderivative, Entit
return HoldsAtSampledPoints(Bare(pinnedAntiderivative).Differentiate(x), pinnedIntegrand, x);
}

/// <summary>
/// Whether the derivative of <paramref name="antiderivative"/> differs from
/// <paramref name="integrand"/> at one of the default points where the integrand is
/// real, as <see cref="DiffersWhereReal"/> tells, with the symbols pinned before the
/// differentiation, as <see cref="DerivativeHoldsAtSampledPoints"/> pins them.
/// </summary>
internal static bool DerivativeDiffersWhereReal(Entity antiderivative, Entity integrand, Variable x)
{
using var _ = MathS.Settings.DowncastingEnabled.Set(false);
var (pinnedAntiderivative, pinnedIntegrand) = Pinned(antiderivative, integrand, x);
return DiffersWhereReal(Bare(pinnedAntiderivative).Differentiate(x), pinnedIntegrand, x);
}

/// <summary>
/// The two with every symbol but <paramref name="x"/> pinned to a fixed value:
/// distinct values, off the integers, so that no two factors coincide by accident and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19089,6 +19089,20 @@ private static Entity SimplifiedWithoutTheImaginaryUnit(Entity written, Entity i
/// Attempts to solve an integral using u-substitution.
/// Looks for patterns where f(g(x)) * g'(x) can be integrated as F(g(x)).
/// </summary>
/// <summary>
/// Whether <paramref name="expr"/> holds an even root of something in <paramref name="x"/>
/// other than <paramref name="radicand"/>: the only way it can be real where the radicand is
/// negative and its own root imaginary.
/// </summary>
private static bool HoldsAnEvenRootBesides(Entity expr, Entity radicand, Entity.Variable x)
{
foreach (var node in expr.Nodes)
if (node is Powf(var @base, Number.Rational power) && power.ERational.Denominator.IsEven
&& @base != radicand && @base.ContainsNode(x))
return true;
return false;
}

internal static Entity? SolveBySubstitution(Entity expr, Entity.Variable x, bool integrateByParts = true)
{
// A rational function over written linear factors with symbols in their
Expand Down Expand Up @@ -19325,6 +19339,17 @@ private static Entity SimplifiedWithoutTheImaginaryUnit(Entity written, Entity i
// radicand, as the factoring below conditions its own.
if (asTheRoot != resultInU)
return Functions.PartialFractions.Bare(asTheRoot).Substitute(uSub, u).Provided(rootOf >= Number.Integer.Zero);
// And a rule below can take u real without reading its sign: `sqrt(u^4)`
// written as `u^2` is the other root wherever u is imaginary. That matters
// only where the integrand is real with the radicand negative, which takes
// a second even root imaginary beside this one, so there the answer is
// checked where the integrand is real: `sqrt(cos(x))/sqrt(1 + sec(x))` under
// `t = cos(x)` and `u = sqrt(t)` came back negated wherever the cosine is
// negative. https://github.com/asc-community/AngouriMath/issues/1581
var answer = resultInU.Substitute(uSub, u);
if (HoldsAnEvenRootBesides(expr, rootOf, x) && Functions.PartialFractions.DerivativeDiffersWhereReal(answer, expr, x))
return Functions.PartialFractions.Bare(answer).Provided(rootOf >= Number.Integer.Zero);
return answer;
}
// Substitute back: replace u with g(x)
return resultInU.Substitute(uSub, u);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,38 @@ public void TheSearchsEvenRootSaysWhereItHolds()
Assert.True(derivative.Substitute("x", 2.8).Evaled.IsNaN, "the answer claims a derivative outside its condition");
}

/// <summary>
/// And where the rules below read no sign at all, the answer is checked where the radicand
/// is negative. <c>sqrt(cos(x))/sqrt(1 + sec(x))</c> is real where the cosine is negative,
/// two of its roots imaginary there; under <c>t = cos(x)</c> and <c>u = sqrt(t)</c> the
/// product of roots below the bar was written with <c>sqrt(u^4) = u^2</c>, and the answer
/// was negated wherever the cosine is negative. Rubi's 4.5.1.2, and the same in <c>t</c>.
/// https://github.com/asc-community/AngouriMath/issues/1581
/// </summary>
[Theory]
[InlineData("sqrt(cos(x))/sqrt(1 + sec(x))", "provided cos(x) >= 0", new[] { 0.3, 0.9, -0.7 }, 2.4)]
[InlineData("sqrt(x)/(sqrt(1/x + 1)*sqrt(1 - x^2))", "provided x >= 0", new[] { 0.2, 0.5, 0.8 }, -0.6)]
public void AnEvenRootTakenRealWithoutASignSaysWhereItHolds(string written, string condition, double[] inside, double outsideAt)
{
var integrand = written.ToEntity();
var integral = integrand.Integrate("x");
Assert.DoesNotContain("integral(", integral.Stringize());
Assert.Contains(condition, integral.Stringize());
var derivative = integral.Substitute("C", 0).Differentiate("x");
foreach (var at in inside)
{
var got = derivative.Substitute("x", at).EvalNumerical();
var want = integrand.Substitute("x", at).EvalNumerical();
Assert.False(got.IsNaN, $"no answer at x = {at}, inside the condition");
Assert.True(Math.Abs((double)(got - want).RealPart) < 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart)),
$"d/dx of the antiderivative is {got} at x = {at}, where the integrand is {want}");
}
// Beyond it the integrand is real and the answer claims nothing.
var outside = integrand.Substitute("x", outsideAt).EvalNumerical();
Assert.True(Math.Abs((double)outside.ImaginaryPart) < 1e-12, $"the integrand at x = {outsideAt} is {outside}, not real");
Assert.True(derivative.Substitute("x", outsideAt).Evaled.IsNaN, "the answer claims a derivative outside its condition");
}

/// <summary>
/// A polynomial over a square root of something linear. None of these had an
/// antiderivative, and each is a first-year exercise.
Expand Down
Loading