From 1ae86b4612ef968125597528f0f20bcebd4c245b Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Fri, 23 Oct 2026 11:01:02 +0000 Subject: [PATCH 1/2] Take the real root in the sampled checks where the library's power takes it The checks at sampled points evaluate both sides in intervals, doubles and then forty and eighty digits, and both tiers took every power's principal branch. The library takes the real root for a negative real base and an exact exponent of odd denominator below 10: (-8)^(1/3) is -2 (#204). So on the negative axis the check could disagree with the values it was checking. The derivative of (-x^3)^(1/3) is -x^2 (-x^3)^(-2/3), which is -1. At the points right of 0 the check read it as -e^(-2 i pi/3), and turned the right answer away. Equally it could accept an answer that is right only on the principal branch, where the library's value is another. Both tiers now take the real root under the conditions Number.Pow takes it: the exponent an exact fraction in the tree, not whole, of odd denominator below 10, and the base's interval real and negative. An even denominator, a decimal exponent and a base off the real line keep the principal branch, as the library's power does. Fixes #2089. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- .../Numerics/IntervalEvaluation.cs | 24 ++++++++++++++++++- .../AngouriMath/Numerics/PreciseEvaluation.cs | 14 ++++++++++- .../UnitTests/Core/IntervalEvaluationTest.cs | 18 ++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/Sources/AngouriMath/Numerics/IntervalEvaluation.cs b/Sources/AngouriMath/Numerics/IntervalEvaluation.cs index a406deb6a..9911662ca 100644 --- a/Sources/AngouriMath/Numerics/IntervalEvaluation.cs +++ b/Sources/AngouriMath/Numerics/IntervalEvaluation.cs @@ -375,6 +375,16 @@ private static ComplexInterval Evaluate(Entity expr) } } + /// + /// The numerator of where it is an exact fraction of odd denominator below 10, to which + /// raises a negative real as its real root; otherwise. + /// + 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) { @@ -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): diff --git a/Sources/AngouriMath/Numerics/PreciseEvaluation.cs b/Sources/AngouriMath/Numerics/PreciseEvaluation.cs index 2eb168b50..02ae830d1 100644 --- a/Sources/AngouriMath/Numerics/PreciseEvaluation.cs +++ b/Sources/AngouriMath/Numerics/PreciseEvaluation.cs @@ -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): diff --git a/Sources/Tests/UnitTests/Core/IntervalEvaluationTest.cs b/Sources/Tests/UnitTests/Core/IntervalEvaluationTest.cs index e7c27a0be..f95ed2877 100644 --- a/Sources/Tests/UnitTests/Core/IntervalEvaluationTest.cs +++ b/Sources/Tests/UnitTests/Core/IntervalEvaluationTest.cs @@ -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)")] @@ -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")] @@ -265,6 +268,21 @@ public void ADerivativeKeepingASpecialFunctionIsChecked(string antiderivative, s Assert.False(Functions.PartialFractions.HoldsAtSampledPoints(derivative, (integrand + " + 1/1000").ToEntity(), x)); } + /// + /// The real root where the library's power takes it, as (-8)^(1/3) is -2: the derivative of + /// (-x^3)^(1/3), -x^2 (-x^3)^(-2/3), is -1, and at the points right of 0 the check read it on + /// the principal branch, -e^(-2 i pi/3), and said it was not. + /// #2089 + /// + [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() { From de0a0e1eb0551ab554d794c54e1ee6be1fb2c324 Mon Sep 17 00:00:00 2001 From: Rafael Vuijk Date: Fri, 23 Oct 2026 13:06:14 +0000 Subject: [PATCH 2/2] Name Number.Pow in code text, which the netstandard2.0 build could not resolve as a cref Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura --- Sources/AngouriMath/Numerics/IntervalEvaluation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/AngouriMath/Numerics/IntervalEvaluation.cs b/Sources/AngouriMath/Numerics/IntervalEvaluation.cs index 9911662ca..aba50555d 100644 --- a/Sources/AngouriMath/Numerics/IntervalEvaluation.cs +++ b/Sources/AngouriMath/Numerics/IntervalEvaluation.cs @@ -377,7 +377,7 @@ private static ComplexInterval Evaluate(Entity expr) /// /// The numerator of where it is an exact fraction of odd denominator below 10, to which - /// raises a negative real as its real root; otherwise. + /// Number.Pow raises a negative real as its real root; otherwise. /// internal static EInteger? TheRealRootIsTaken(Entity exponent) => exponent is Number.Rational { ERational: var exact } and not Number.Integer