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
16 changes: 16 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,22 @@ the working digits, or `MathS.Settings.PrecisionErrorZeroRange` where a caller h
still that integer, so `e^(-123.456)` is 0 as it was.
[#1338](https://github.com/asc-community/AngouriMath/issues/1338).

### A piecewise is defined where the case it takes is

**Domain conditions that were false.** The domain condition of a piecewise asked every case's predicate
to be defined, and an ordering is defined only between reals: `piecewise(1 provided not i in RR, 2 provided i < 0)`
is `1`, and its domain condition was `false`. It is where some case's predicate holds, no earlier one does
-- a predicate with no value passed over, as the simplification passes over it -- and that case's expression
is defined. An antiderivative with an arm for each sign of a discriminant and one for its not being real
carried the discriminant's being real in its condition, and held nowhere when it was not
([#1817](https://github.com/asc-community/AngouriMath/issues/1817)).

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"piecewise(1 provided not i in RR, 2 provided i < 0)".ToEntity().DomainCondition` | `false` | `true` |
| `"piecewise(2 * x provided not x = 0, 3 * x ^ 2 provided i - 1 < 0)".ToEntity().DomainCondition` | `false` | `not x = 0` |
| `"1/(sqrt(a + i*a*x)*(c + d*x)^(3/2)*(a - i*a*x))".ToEntity().Integrate("x")` | `integral(...)`; on the unreleased master, an answer with no value at any point | 3,267 characters, defined |

### A piecewise case whose condition is undefined is passed over

**A different answer.** A case whose predicate evaluates to NaN -- an order comparison of a number
Expand Down
34 changes: 32 additions & 2 deletions Sources/AngouriMath/Functions/Evaluation/Evaluation.Definition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,45 @@ partial record Entity
/// - Singularities and poles (points where a function is undefined)
/// - Piecewise continuity (tracking where discontinuities occur)
/// </remarks>
public Entity DomainCondition => domainCondition.GetValue(static @this => ScopedToItsBinder(@this, @this.DirectChildren.Aggregate(@this.IntrinsicCondition, (accum, curr) =>
public Entity DomainCondition => domainCondition.GetValue(static @this => (@this is Piecewise piecewise ? WhereTheCaseItTakesIsDefined(piecewise)
: ScopedToItsBinder(@this, @this.DirectChildren.Aggregate(@this.IntrinsicCondition, (accum, curr) =>
(accum, curr.DomainCondition) switch {
(Boolean(true), Boolean(true)) => Boolean.True,
(var l, Boolean(true)) => l,
(Boolean(true), var r) => r,
(var l, var r) => l & r,
})), this).InnerSimplified;
}))), this).InnerSimplified;
private LazyPropertyA<Entity> domainCondition;

/// <summary>
/// Where a piecewise is defined: where some case's predicate holds, no earlier one does,
/// and that case's expression is defined. An earlier predicate that has no value -- <c>i &lt; 0</c>,
/// the complex numbers not being ordered -- is passed over, as
/// <see cref="Piecewise"/>'s simplification passes over it.
/// </summary>
/// <remarks>
/// The domain condition of every child, conjoined, asked each predicate to be defined, and
/// <c>c &lt; 0</c> is defined only where <c>c</c> is real: <c>piecewise(1 provided not i in RR, 2 provided i &lt; 0)</c>
/// is 1 and its domain condition was false, and an antiderivative with an arm for each sign of
/// a discriminant and one for its not being real held nowhere when the discriminant was not.
/// https://github.com/asc-community/AngouriMath/issues/1817
/// </remarks>
private static Entity WhereTheCaseItTakesIsDefined(Piecewise piecewise)
{
Entity defined = Boolean.False;
Entity noEarlierCase = Boolean.True;
foreach (var @case in piecewise.Cases)
{
// Never taken, and passed over by the cases after it.
if (@case.Predicate.Evaled is var decided && (decided.IsNaN || decided == Boolean.False))
continue;
var predicateDomain = @case.Predicate.DomainCondition;
defined |= @case.Predicate & @case.Expression.DomainCondition & noEarlierCase;
noEarlierCase &= predicateDomain == Boolean.True ? !@case.Predicate : !@case.Predicate | !predicateDomain;
}
return defined;
}

/// <summary>
/// <paramref name="condition"/> with each conjunct that mentions a name
/// <paramref name="binder"/> binds read the way the binder reads it: required at every
Expand Down
32 changes: 32 additions & 0 deletions Sources/Tests/UnitTests/Core/UndefinedPiecewiseConditionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,37 @@ public void TheDerivativeIsThatOfTheCaseThatHolds()
var derivative = "piecewise(x ^ 2 provided not x = 0, x ^ 3 provided -13/10 * i - 3/5 < 0)".ToEntity().Differentiate("x");
Near(0.6, derivative.Substitute("x", 0.3));
}

// Where the piecewise is defined: where the case it takes is, an undefined predicate before
// it passed over. https://github.com/asc-community/AngouriMath/issues/1817
[Theory]
[InlineData("piecewise(1 provided not i in RR, 2 provided i < 0)", "true")]
[InlineData("piecewise(1 provided i < 0, 2 provided 1 > 0)", "true")]
[InlineData("piecewise(2 * x provided not x = 0, 3 * x ^ 2 provided i - 1 < 0)", "not x = 0")]
[InlineData("piecewise(1 provided i < 0, 2 provided i > 0)", "false")]
public void TheDomainIsWhereTheCaseItTakesIsDefined(string piecewise, string domain)
=> Assert.Equal(domain.ToEntity().InnerSimplified, piecewise.ToEntity().DomainCondition);

// Off the real line: the arm for the quantity not being real is taken, and the domain does
// not ask the comparisons in the other arms to be defined.
[Theory]
[InlineData(0.7, 1.07)]
[InlineData(-0.7, 2.0)]
public void AnArmForANonRealQuantityKeepsTheDomain(double c, double d)
{
var domain = "piecewise(1 provided not (c + i*d) in RR, 2 provided c + i*d < 0, 3 provided c + i*d > 0)".ToEntity().DomainCondition;
Assert.Equal(Entity.Boolean.True, domain.Substitute("c", c).Substitute("d", d).Evaled);
}

// A case taken where its expression has no value leaves the piecewise undefined there,
// whatever the cases after it say.
[Fact]
public void TheFirstCaseThatHoldsDecides()
{
var domain = "piecewise(1/x provided x > -1, 0 provided true)".ToEntity().DomainCondition;
Assert.Equal(Entity.Boolean.False, domain.Substitute("x", 0).Evaled);
Assert.Equal(Entity.Boolean.True, domain.Substitute("x", -2).Evaled);
Assert.Equal(Entity.Boolean.True, domain.Substitute("x", 1).Evaled);
}
}
}
Loading