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
24 changes: 23 additions & 1 deletion Sources/AngouriMath/Numerics/IntervalEvaluation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,16 @@ private static ComplexInterval Evaluate(Entity expr)
}
}

/// <summary>
/// The numerator of <paramref name="exponent"/> where it is an exact fraction of odd denominator below 10, to which
/// <c>Number.Pow</c> raises a negative real as its real root; <see langword="null"/> otherwise.
/// </summary>
internal static EInteger? TheRealRootIsTaken(Entity exponent)
=> exponent is Number.Rational { ERational: var exact } and not Number.Integer
&& exact.Denominator.Abs() is var q && !q.IsEven && q.CompareTo(EInteger.FromInt32(10)) < 0
? (exact.Denominator.Sign < 0 ? exact.Numerator.Negate() : exact.Numerator)
: null;

[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
private static ComplexInterval EvaluateOtherThanArithmetic(Entity expr)
{
Expand All @@ -387,9 +397,21 @@ private static ComplexInterval EvaluateOtherThanArithmetic(Entity expr)
case Variable variable when variable == MathS.e:
return ComplexInterval.Real(Interval.Around(Math.E));
case Powf(var @base, var exponent):
{
if (@base == MathS.e)
return Evaluate(exponent).Exp();
return Evaluate(@base).Pow(Evaluate(exponent));
var evaluatedBase = Evaluate(@base);
// The real root where the library's power takes it: a negative real base and an exact exponent of
// odd denominator below 10, as (-8)^(1/3) is -2. Principal there, the derivative of (-x^3)^(1/3)
// was -e^(-2 i pi/3) at x > 0 where it is -1, and the check turned the right answer away.
// https://github.com/asc-community/AngouriMath/issues/2089
if (TheRealRootIsTaken(exponent) is { } numerator && evaluatedBase.IsReal && evaluatedBase.Re.High < 0)
{
var modulus = ComplexInterval.Real(-evaluatedBase.Re).Pow(Evaluate(exponent));
return numerator.IsEven ? modulus : -modulus;
}
return evaluatedBase.Pow(Evaluate(exponent));
}
case Sinf(var argument):
return Evaluate(argument).Sin();
case Cosf(var argument):
Expand Down
14 changes: 13 additions & 1 deletion Sources/AngouriMath/Numerics/PreciseEvaluation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,19 @@ private PreciseComplexInterval EvaluateOtherThanArithmetic(Entity expr)
case Variable variable when variable == MathS.e:
return Real(E());
case Powf(var @base, var exponent):
return @base == MathS.e ? Exp(Evaluate(exponent)) : Pow(Evaluate(@base), Evaluate(exponent));
{
if (@base == MathS.e)
return Exp(Evaluate(exponent));
var evaluatedBase = Evaluate(@base);
// The real root where the library's power takes it, as in the doubles.
// https://github.com/asc-community/AngouriMath/issues/2089
if (IntervalEvaluation.TheRealRootIsTaken(exponent) is { } numerator && evaluatedBase.IsReal && evaluatedBase.Re.High.Sign < 0)
{
var modulus = Pow(Real(evaluatedBase.Re.Negate()), Evaluate(exponent));
return numerator.IsEven ? modulus : Negate(modulus);
}
return Pow(evaluatedBase, Evaluate(exponent));
}
case Sinf(var argument):
return Sin(Evaluate(argument));
case Cosf(var argument):
Expand Down
18 changes: 18 additions & 0 deletions Sources/Tests/UnitTests/Core/IntervalEvaluationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public sealed class IntervalEvaluationTest
[InlineData("arcsec(3)")]
[InlineData("arccsc(-2)")]
[InlineData("(2/3)^(1/3)")]
[InlineData("(-8)^(1/3) + (-2)^(2/5)")]
[InlineData("(-3/2)^(-4/7) + (-5)^(1/2)")]
[InlineData("(1 + 2i)^3")]
[InlineData("1/(3 + 4i)")]
[InlineData("e^(i * pi/4)")]
Expand Down Expand Up @@ -182,6 +184,7 @@ public void TheSizeOfALargeComplexValueIsFinite()
[InlineData("sin(1 + i) * cos(2 - 3i)")]
[InlineData("ln(-2 + i) + arctan(2) - arcsin(1/3)")]
[InlineData("(2/3)^(1/3) + sec(1) + csc(2)")]
[InlineData("(-8)^(1/3) + (-2)^(2/5) + (-3/2)^(-4/7) + (-5)^(1/2)")]
[InlineData("arccot(-2) + arcsec(3) + arccsc(-2) + arccot(0)")]
[InlineData("sin(10^6) + cos(10^6)")]
[InlineData("sqrt(2) provided 1 < 2")]
Expand Down Expand Up @@ -265,6 +268,21 @@ public void ADerivativeKeepingASpecialFunctionIsChecked(string antiderivative, s
Assert.False(Functions.PartialFractions.HoldsAtSampledPoints(derivative, (integrand + " + 1/1000").ToEntity(), x));
}

/// <summary>
/// The real root where the library's power takes it, as <c>(-8)^(1/3)</c> is <c>-2</c>: the derivative of
/// <c>(-x^3)^(1/3)</c>, <c>-x^2 (-x^3)^(-2/3)</c>, is <c>-1</c>, and at the points right of 0 the check read it on
/// the principal branch, <c>-e^(-2 i pi/3)</c>, and said it was not.
/// <a href="https://github.com/asc-community/AngouriMath/issues/2089">#2089</a>
/// </summary>
[Fact]
public void TheRealRootIsTheOneThePowerTakes()
{
var x = MathS.Var("x");
var derivative = "(-x^3)^(1/3)".ToEntity().Differentiate(x);
Assert.True(Functions.PartialFractions.HoldsAtSampledPoints(derivative, "-1".ToEntity(), x));
Assert.False(Functions.PartialFractions.HoldsAtSampledPoints(derivative, "-1 + 1/1000".ToEntity(), x));
}

[Fact]
public void AgreementHasThreeOutcomes()
{
Expand Down
Loading