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 @@ -4292,6 +4292,21 @@ Both columns measured on a build, `v2.5.0` against this change.
| `"x^2/x^2"` | `1 provided not x ^ 2 = 0` | `1 provided not x = 0` |
| `"(x^2 - 1)/(x^2 + 2x + 1) + 1/(x + 1)"` | `x / (x + 1) provided not x ^ 2 + 2 * x + 1 = 0` | `x / (x + 1)` |

### A coefficient past the doubles is not taken for zero

**Answers where there were none.** `(b x^(-4) + b x^(-6) + b x^(-8))/sqrt(1/c^2 + x^2)` was declined. Since the interval
evaluation of [#1019](https://github.com/asc-community/AngouriMath/issues/1019) it was answered on the unreleased
master with two of its three terms missing, wrong at every point. Its coefficients run to `c^294`, past `1.3e154` at
the symbols a coefficient is pinned to when it is read in lowest terms. The check that two values agree took their
sizes squared, which is infinite there, and so was the tolerance relative to them. Every two values that large agreed,
and each of those coefficients was taken for zero. The sizes are taken without squaring now, and a size past the
doubles is left to the decimals.

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"(b*x^(-4) + b*x^(-6) + b*x^(-8))*(1/c^2 + x^2)^(-1/2)".ToEntity().Integrate("x")` | `integral(...)` | 1,445 characters |
| `"(b*x^12 + b*x^10 + b*x^8)*(1/c^2 + x^2)^(-1/2)/x^16".ToEntity().Integrate("x")` | `integral(...)` | 1,254 characters |

### A partial-fraction coefficient with symbols in it is in lowest terms, its rational content included

**Improvement, not silent.** The decomposition over written factors with symbols among their
Expand Down
31 changes: 28 additions & 3 deletions Sources/AngouriMath/Numerics/IntervalEvaluation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,21 @@ internal ComplexInterval(Interval re, Interval im)

internal bool IsFinite => Re.IsFinite && Im.IsFinite;
internal bool IsReal => Im.Low == 0 && Im.High == 0;
internal double Magnitude => Math.Sqrt(Re.Magnitude * Re.Magnitude + Im.Magnitude * Im.Magnitude);
internal double Magnitude => Hypotenuse(Re.Magnitude, Im.Magnitude);

/// <summary>
/// <c>sqrt(a^2 + b^2)</c> for <paramref name="a"/> and <paramref name="b"/> at least zero,
/// scaled by the larger so that it does not overflow: squared, anything past about
/// <c>1.3e154</c> is infinite.
/// </summary>
internal static double Hypotenuse(double a, double b)
{
var (large, small) = a >= b ? (a, b) : (b, a);
if (large == 0 || !Interval.IsFiniteDouble(large))
return large;
var ratio = small / large;
return large * Math.Sqrt(1 + ratio * ratio);
}

public static ComplexInterval operator +(ComplexInterval a, ComplexInterval b) => new(a.Re + b.Re, a.Im + b.Im);
public static ComplexInterval operator -(ComplexInterval a, ComplexInterval b) => new(a.Re - b.Re, a.Im - b.Im);
Expand Down Expand Up @@ -308,15 +322,26 @@ internal static class IntervalEvaluation
/// of one, where both are smaller): true where the whole of their difference is within
/// it, false where the whole of it is beyond, null where the intervals cannot tell.
/// </summary>
/// <remarks>
/// The sizes are taken without squaring. Squared, a size past about <c>1.3e154</c> was
/// infinite, and so was the tolerance relative to it, and any two values that large agreed:
/// a coefficient of <c>c^294</c> read at <c>c = 3.71</c> was taken for zero, which dropped
/// two of the three terms of <c>(b x^(-4) + b x^(-6) + b x^(-8))/sqrt(1/c^2 + x^2)</c>'s
/// antiderivative, and an answer that large at a sampled point was passed whatever it was.
/// A size past the doubles is left to the decimals.
/// https://github.com/asc-community/AngouriMath/issues/1019
/// </remarks>
internal static bool? Agree(ComplexInterval left, ComplexInterval right, double relativeTolerance)
{
var difference = left - right;
var scale = Math.Max(1.0, Math.Max(left.Magnitude, right.Magnitude));
if (!Interval.IsFiniteDouble(scale))
return null;
var allowed = relativeTolerance * scale;
var largest = Math.Sqrt(difference.Re.Magnitude * difference.Re.Magnitude + difference.Im.Magnitude * difference.Im.Magnitude);
var largest = Interval.Wider(ComplexInterval.Hypotenuse(difference.Re.Magnitude, difference.Im.Magnitude), 1);
if (largest <= allowed)
return true;
var smallest = Math.Sqrt(difference.Re.Mignitude * difference.Re.Mignitude + difference.Im.Mignitude * difference.Im.Mignitude);
var smallest = Interval.Wider(ComplexInterval.Hypotenuse(difference.Re.Mignitude, difference.Im.Mignitude), -1);
if (smallest > allowed)
return false;
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// Copyright (c) 2019-2026 Angouri.
// AngouriMath is licensed under MIT.
// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
// Website: https://am.angouri.org.
//

using System;
using AngouriMath.Extensions;
using Xunit;

namespace AngouriMath.Tests.Calculus
{
/// <summary>
/// The coefficients of these antiderivatives run to <c>c^294</c>, past <c>1.3e154</c> at the
/// symbols the lowest-terms reading pins, where the interval check took a size squared to an
/// infinity and agreed that each was zero: two of the three terms were dropped, and the answer
/// differed from the integral at every point.
/// <a href="https://github.com/asc-community/AngouriMath/issues/1019">#1019</a>
/// </summary>
[Trait("Area", "Calculus")]
public sealed class ACoefficientPastTheDoublesIsNotTakenForZeroIntegralTest
{
[Theory]
[InlineData("(b*x^(-4) + b*x^(-6) + b*x^(-8))*(1/c^2 + x^2)^(-1/2)")]
[InlineData("(b*x^12 + b*x^10 + b*x^8)*(1/c^2 + x^2)^(-1/2)/x^16")]
public void KeepsEveryTerm(string integrand)
{
var integral = integrand.ToEntity().Integrate("x");
if (integral.Stringize().Contains("integral("))
return;
Entity Pinned(Entity e) => e.Substitute("b", 0.7).Substitute("c", 1.3);
var derivative = Pinned(integral.Substitute("C", 0)).Differentiate("x");
var original = Pinned(integrand.ToEntity());
foreach (var at in new[] { -2.1, -1.3, -0.6, 0.5, 1.2, 2.3 })
{
var want = original.Substitute("x", at).EvalNumerical();
var got = derivative.Substitute("x", at).EvalNumerical();
Assert.True(Math.Abs((double)(got - want).RealPart) + Math.Abs((double)(got - want).ImaginaryPart)
< 1e-9 * Math.Max(1, Math.Abs((double)want.RealPart)),
$"d/dx of the antiderivative of {integrand} is {got} at x = {at}, where the integrand is {want}");
}
}
}
}
16 changes: 16 additions & 0 deletions Sources/Tests/UnitTests/Core/IntervalEvaluationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,22 @@ public void MoreDigitsSettleWhatDoublesCannot()
Assert.False(PreciseEvaluation.Agree(PreciseEvaluation.Of(nearby, 40)!.Value, PreciseEvaluation.Of(sine, 40)!.Value, 1e-9));
}

/// <summary>
/// A size past about <c>1.3e154</c> squared to an infinity, and so did the tolerance relative
/// to it: two values that large agreed whatever they were, and a coefficient that large was
/// taken for zero. Neither is decided now, and an exact difference between them is seen.
/// </summary>
[Theory]
[InlineData("2 * 10^167", "0")]
[InlineData("2 * 10^167", "3 * 10^167")]
[InlineData("2 * 10^167 * i", "0")]
public void ASizePastTheDoublesDoesNotAgree(string left, string right)
=> Assert.NotEqual(true, IntervalEvaluation.Agree(IntervalEvaluation.Of(left.ToEntity())!.Value, IntervalEvaluation.Of(right.ToEntity())!.Value, 1e-30));

[Fact]
public void TheSizeOfALargeComplexValueIsFinite()
=> Assert.InRange(IntervalEvaluation.Of("3 * 10^160 + 4 * 10^160 * i".ToEntity())!.Value.Magnitude, 4.99e160, 5.01e160);

[Theory]
[InlineData("sqrt(2)")]
[InlineData("e^2 - pi")]
Expand Down
Loading