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 @@ -151,6 +151,21 @@ an arm for each sign of a quantity that is not real, `x^2 provided not x = 0` be
| `"piecewise(2 * x provided not x = 0, 3 * x ^ 2 provided i - 1 < 0)".ToEntity().InnerSimplified` | `NaN` | `2 * x` where `x` is not 0 |
| `"piecewise(1 provided i < 0, 2 provided i > 0)".ToEntity().InnerSimplified` | `NaN` | `NaN`, as before: no case holds |

### Zero times a power with a non-real exponent is zero where the power is defined

**A value where there was NaN.** `0 b^i` simplified to NaN, and so did `b^i - b^i`. That in turn made `x b^i`'s
derivative NaN, along with the derivative of every antiderivative with a constant like `(e^a)^i` in it. A power's
condition was `not b = 0 or w > 0`, and `w > 0` has no value for a non-real `w`, since the complex numbers are not
ordered. `0^w` is defined where `Re(w) > 0`, and that is the condition now
([#1865](https://github.com/asc-community/AngouriMath/issues/1865)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"0 * b^i".ToEntity().Simplify()` | `NaN` | `0 provided not b = 0` |
| `"b^i - b^i".ToEntity().Simplify()` | `NaN` | `0 provided not b = 0` |
| `"0 * b^(2 + i)".ToEntity().Simplify()` | `NaN` | `0` |
| `"x * b^i".ToEntity().Differentiate("x")` | `NaN` | `b ^ i provided not b = 0` |

### The factorial far from zero, and a quotient by a number whose modulus is nearly whole

The numerical factorial is Spouge's approximation, and it holds right of zero. Used left of zero it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,19 @@ public partial record Powf
// https://github.com/asc-community/AngouriMath/issues/721
private protected override Entity IntrinsicCondition =>
Codomain < Domain.Complex && IsEvenRoot
? Base >= 0 & (!Base.EqualTo(0) | Exponent > 0)
: (!Base.EqualTo(0) | Exponent > 0);
? Base >= 0 & WhereZeroIsDefined
: WhereZeroIsDefined;

/// <summary>
/// <c>0^w</c> is defined where <c>Re(w) &gt; 0</c>. For a non-real number <c>w</c>,
/// <c>w &gt; 0</c> has no value, the complex numbers not being ordered, and made the
/// whole condition NaN, so <c>0 b^i</c> was NaN; its real part is read instead.
/// <a href="https://github.com/asc-community/AngouriMath/issues/1865">#1865</a>
/// </summary>
private Entity WhereZeroIsDefined
=> (Exponent is Complex ? Exponent : Exponent.Vars.Any() ? null : Exponent.Evaled) is Complex exponent and not Real
? exponent.RealPart.IsPositive ? Boolean.True : !Base.EqualTo(0)
: !Base.EqualTo(0) | Exponent > 0;

/// <summary>
/// Whether the exponent is a literal rational whose denominator is even — the case
Expand Down
35 changes: 35 additions & 0 deletions Sources/Tests/UnitTests/Calculus/DerivativeGapsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,40 @@ public void AntiderivativeOfAPowerDifferentiatesBackEverywhere()
// only in how the condition nests.
public void SymbolicExponentsAreUnaffected(string input, string expected) =>
Assert.Equal(expected, input.ToEntity().Differentiate("x").Simplify().Stringize());

// `0^w` is defined where `Re(w) > 0`. A power's condition was `not b = 0 or w > 0`,
// NaN for a non-real `w`, so zero times `b^i` was NaN and so was the derivative of
// anything with a constant `b^i` in it.
// https://github.com/asc-community/AngouriMath/issues/1865
[Theory]
[InlineData("0 * b ^ i", "0 provided not b = 0")]
[InlineData("b ^ i - b ^ i", "0 provided not b = 0")]
[InlineData("0 * b ^ (i / 2)", "0 provided not b = 0")]
[InlineData("0 * b ^ (-1 + i)", "0 provided not b = 0")]
[InlineData("0 * b ^ (2 + i)", "0")]
[InlineData("0 ^ i", "NaN")]
public void ZeroTimesAPowerWithANonRealExponent(string input, string expected) =>
Assert.Equal(expected, input.ToEntity().Simplify().Stringize());

[Theory]
[InlineData("x * b ^ i")]
[InlineData("sec(a + i * ln(x)) ^ 3")]
[InlineData("x * sin(a + ln(c * x) * sqrt(-4))")]
public void AConstantWithANonRealExponentDifferentiates(string integrand)
{
var f = integrand.ToEntity();
var antiderivative = integrand.StartsWith("x * b") ? "x ^ 2 / 2 * b ^ i".ToEntity() : f.Integrate("x").Substitute("C", 0);
// Differentiated while still symbolic, then pinned: pinning first never saw the NaN.
var derivative = antiderivative.Differentiate("x");
Entity Pinned(Entity e) => e.Substitute("a", "1/4".ToEntity()).Substitute("b", 2).Substitute("c", 2);
foreach (var point in new[] { "0.57", "1.29" })
{
var want = Pinned(f).Substitute("x", point.ToEntity()).EvalNumerical();
var got = Pinned(derivative).Substitute("x", point.ToEntity()).EvalNumerical();
var difference = (got - want).EvalNumerical();
Assert.True(System.Math.Abs((double)difference.RealPart) + System.Math.Abs((double)difference.ImaginaryPart) < 1e-9,
$"d/dx of the antiderivative of {integrand} is {got} at x = {point}, where it is {want}");
}
}
}
}
Loading