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
18 changes: 18 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,24 @@ interpreter leaves them as written ([#2195](https://github.com/asc-community/Ang
| `"valuation(x, 2)".ToEntity().Compile("x").Call(12)` | `UncompilableNodeException` | `(2, 0)` |
| `"valuation(x, 2)".ToEntity().Compile<double, double>("x")(0)` | `UncompilableNodeException` | `Infinity` |

### Compiling over the reals can refuse what is not real

**Not silent, and only where asked for: `MathS.Settings.RefuseNonRealCompilation`, off by default, refuses to compile into a
function returning `double` or `float` an expression that is not real wherever it is defined.** Over the reals every value
computed on the way is real, so `sin(arcsin(x))` at 2, which is 2, is NaN there, as `1/x` is at 0. That is now documented
on every `Compile` overload, and with the setting on, such an expression throws `UncompilableNodeException`, so that a NaN
means only that there is no value. The check is structural and conservative: `sqrt(x)` and `ln(x)` are refused too.

**Silent: `floor`, `ceil`, `round`, `min` and `max` of real arguments are known to be real**, which the derivative of a
modulus or a sign needs, so these are answered where they were left unevaluated
([#2286](https://github.com/asc-community/AngouriMath/issues/2286)).

| Input | Was (2.6.0) | Now |
|---|---|---|
| `"x^y".Compile<double, double, double>("x", "y")` under the setting | (no setting) | throws `UncompilableNodeException` |
| `"derivative(abs(floor(x)), x)".ToEntity().Simplify()` | unevaluated | `0 provided not floor(x) = 0 and not x in ZZ` |
| `"derivative(abs(min(x, 1)), x)".ToEntity().Simplify()` | unevaluated | `sgn(min(x, 1)) * (1 - sgn(x - 1)) / 2 provided not min(x, 1) = 0 and not x - 1 = 0` |

### Membership and divisibility compile

**Not silent: what was refused is compiled.** `x in S` and `a divides b` were refused by both compilers with
Expand Down
3 changes: 3 additions & 0 deletions Sources/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1013,5 +1013,8 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed
[Tests/UnitTests/Common/DivisionByZeroOverDoubleTest.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n

[Tests/UnitTests/Common/RefuseNonRealCompilationTest.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n

[Tests/UnitTests/Common/BranchCutCompilationTest.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n
45 changes: 45 additions & 0 deletions Sources/AngouriMath/Convenience/CompilationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ public static partial class AngouriMathExtensions
/// 3
/// </code>
/// </example>
/// <remarks>
/// Into a function that returns <see cref="double"/> or <see cref="float"/>, a value that is not real is NaN, and
/// so is every value computed from one, though the expression may be real there: <c>sin(arcsin(x))</c> at 2.
/// <see cref="MathS.Settings.RefuseNonRealCompilation"/> refuses such an expression instead.
/// </remarks>
public static TDelegate Compile<TDelegate>(this string @this, CompilationProtocol protocol, Type returnType, IEnumerable<(Type type, Variable variable)> typesAndNames) where TDelegate : Delegate
=> IntoLinqCompiler.Compile<TDelegate>(@this, returnType, protocol, typesAndNames);

Expand Down Expand Up @@ -86,6 +91,11 @@ public static TDelegate Compile<TDelegate>(this string @this, CompilationProtoco
/// 10
/// </code>
/// </example>
/// <remarks>
/// Into a function that returns <see cref="double"/> or <see cref="float"/>, a value that is not real is NaN, and
/// so is every value computed from one, though the expression may be real there: <c>sin(arcsin(x))</c> at 2.
/// <see cref="MathS.Settings.RefuseNonRealCompilation"/> refuses such an expression instead.
/// </remarks>
public static Func<TIn1, TOut> Compile<TIn1, TOut>(this string @this, Variable var1)
=> IntoLinqCompiler.Compile<Func<TIn1, TOut>>(@this, typeof(TOut), new(),
new[] { (typeof(TIn1), var1) });
Expand Down Expand Up @@ -126,6 +136,11 @@ public static Func<TIn1, TOut> Compile<TIn1, TOut>(this string @this, Variable v
/// 30
/// </code>
/// </example>
/// <remarks>
/// Into a function that returns <see cref="double"/> or <see cref="float"/>, a value that is not real is NaN, and
/// so is every value computed from one, though the expression may be real there: <c>sin(arcsin(x))</c> at 2.
/// <see cref="MathS.Settings.RefuseNonRealCompilation"/> refuses such an expression instead.
/// </remarks>
public static Func<TIn1, TIn2, TOut> Compile<TIn1, TIn2, TOut>(this string @this, Variable var1, Variable var2)
=> IntoLinqCompiler.Compile<Func<TIn1, TIn2, TOut>>(@this, typeof(TOut), new(),
new[] { (typeof(TIn1), var1), (typeof(TIn2), var2) });
Expand Down Expand Up @@ -172,6 +187,11 @@ public static Func<TIn1, TIn2, TOut> Compile<TIn1, TIn2, TOut>(this string @this
/// 60
/// </code>
/// </example>
/// <remarks>
/// Into a function that returns <see cref="double"/> or <see cref="float"/>, a value that is not real is NaN, and
/// so is every value computed from one, though the expression may be real there: <c>sin(arcsin(x))</c> at 2.
/// <see cref="MathS.Settings.RefuseNonRealCompilation"/> refuses such an expression instead.
/// </remarks>
public static Func<TIn1, TIn2, TIn3, TOut> Compile<TIn1, TIn2, TIn3, TOut>(this string @this, Variable var1, Variable var2, Variable var3)
=> IntoLinqCompiler.Compile<Func<TIn1, TIn2, TIn3, TOut>>(@this, typeof(TOut), new(),
new[] { (typeof(TIn1), var1), (typeof(TIn2), var2) , (typeof(TIn3), var3) });
Expand Down Expand Up @@ -224,6 +244,11 @@ public static Func<TIn1, TIn2, TIn3, TOut> Compile<TIn1, TIn2, TIn3, TOut>(this
/// 100
/// </code>
/// </example>
/// <remarks>
/// Into a function that returns <see cref="double"/> or <see cref="float"/>, a value that is not real is NaN, and
/// so is every value computed from one, though the expression may be real there: <c>sin(arcsin(x))</c> at 2.
/// <see cref="MathS.Settings.RefuseNonRealCompilation"/> refuses such an expression instead.
/// </remarks>
public static Func<TIn1, TIn2, TIn3, TIn4, TOut> Compile<TIn1, TIn2, TIn3, TIn4, TOut>(this string @this, Variable var1, Variable var2, Variable var3, Variable var4)
=> IntoLinqCompiler.Compile<Func<TIn1, TIn2, TIn3, TIn4, TOut>>(@this, typeof(TOut), new(),
new[] { (typeof(TIn1), var1), (typeof(TIn2), var2) , (typeof(TIn3), var3) , (typeof(TIn4), var4) });
Expand Down Expand Up @@ -282,6 +307,11 @@ public static Func<TIn1, TIn2, TIn3, TIn4, TOut> Compile<TIn1, TIn2, TIn3, TIn4,
/// 150
/// </code>
/// </example>
/// <remarks>
/// Into a function that returns <see cref="double"/> or <see cref="float"/>, a value that is not real is NaN, and
/// so is every value computed from one, though the expression may be real there: <c>sin(arcsin(x))</c> at 2.
/// <see cref="MathS.Settings.RefuseNonRealCompilation"/> refuses such an expression instead.
/// </remarks>
public static Func<TIn1, TIn2, TIn3, TIn4, TIn5, TOut> Compile<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>(this string @this, Variable var1, Variable var2, Variable var3, Variable var4, Variable var5)
=> IntoLinqCompiler.Compile<Func<TIn1, TIn2, TIn3, TIn4, TIn5, TOut>>(@this, typeof(TOut), new(),
new[] { (typeof(TIn1), var1), (typeof(TIn2), var2) , (typeof(TIn3), var3) , (typeof(TIn4), var4) , (typeof(TIn5), var5) });
Expand Down Expand Up @@ -346,6 +376,11 @@ public static Func<TIn1, TIn2, TIn3, TIn4, TIn5, TOut> Compile<TIn1, TIn2, TIn3,
/// 210
/// </code>
/// </example>
/// <remarks>
/// Into a function that returns <see cref="double"/> or <see cref="float"/>, a value that is not real is NaN, and
/// so is every value computed from one, though the expression may be real there: <c>sin(arcsin(x))</c> at 2.
/// <see cref="MathS.Settings.RefuseNonRealCompilation"/> refuses such an expression instead.
/// </remarks>
public static Func<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut> Compile<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut>(this string @this, Variable var1, Variable var2, Variable var3, Variable var4, Variable var5, Variable var6)
=> IntoLinqCompiler.Compile<Func<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut>>(@this, typeof(TOut), new(),
new[] { (typeof(TIn1), var1), (typeof(TIn2), var2) , (typeof(TIn3), var3) , (typeof(TIn4), var4) , (typeof(TIn5), var5) , (typeof(TIn6), var6) });
Expand Down Expand Up @@ -416,6 +451,11 @@ public static Func<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TOut> Compile<TIn1, TIn2,
/// 280
/// </code>
/// </example>
/// <remarks>
/// Into a function that returns <see cref="double"/> or <see cref="float"/>, a value that is not real is NaN, and
/// so is every value computed from one, though the expression may be real there: <c>sin(arcsin(x))</c> at 2.
/// <see cref="MathS.Settings.RefuseNonRealCompilation"/> refuses such an expression instead.
/// </remarks>
public static Func<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TIn7, TOut> Compile<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TIn7, TOut>(this string @this, Variable var1, Variable var2, Variable var3, Variable var4, Variable var5, Variable var6, Variable var7)
=> IntoLinqCompiler.Compile<Func<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TIn7, TOut>>(@this, typeof(TOut), new(),
new[] { (typeof(TIn1), var1), (typeof(TIn2), var2) , (typeof(TIn3), var3) , (typeof(TIn4), var4) , (typeof(TIn5), var5) , (typeof(TIn6), var6) , (typeof(TIn7), var7) });
Expand Down Expand Up @@ -492,6 +532,11 @@ public static Func<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TIn7, TOut> Compile<TIn1,
/// 360
/// </code>
/// </example>
/// <remarks>
/// Into a function that returns <see cref="double"/> or <see cref="float"/>, a value that is not real is NaN, and
/// so is every value computed from one, though the expression may be real there: <c>sin(arcsin(x))</c> at 2.
/// <see cref="MathS.Settings.RefuseNonRealCompilation"/> refuses such an expression instead.
/// </remarks>
public static Func<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TIn7, TIn8, TOut> Compile<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TIn7, TIn8, TOut>(this string @this, Variable var1, Variable var2, Variable var3, Variable var4, Variable var5, Variable var6, Variable var7, Variable var8)
=> IntoLinqCompiler.Compile<Func<TIn1, TIn2, TIn3, TIn4, TIn5, TIn6, TIn7, TIn8, TOut>>(@this, typeof(TOut), new(),
new[] { (typeof(TIn1), var1), (typeof(TIn2), var2) , (typeof(TIn3), var3) , (typeof(TIn4), var4) , (typeof(TIn5), var5) , (typeof(TIn6), var6) , (typeof(TIn7), var7) , (typeof(TIn8), var8) });
Expand Down
10 changes: 10 additions & 0 deletions Sources/AngouriMath/Convenience/CompilationExtensions.tt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ namespace AngouriMath.Extensions
/// 3
/// </code>
/// </example>
/// <remarks>
/// Into a function that returns <see cref="double"/> or <see cref="float"/>, a value that is not real is NaN, and
/// so is every value computed from one, though the expression may be real there: <c>sin(arcsin(x))</c> at 2.
/// <see cref="MathS.Settings.RefuseNonRealCompilation"/> refuses such an expression instead.
/// </remarks>
public static TDelegate Compile<TDelegate>(this string @this, CompilationProtocol protocol, Type returnType, IEnumerable<(Type type, Variable variable)> typesAndNames) where TDelegate : Delegate
=> IntoLinqCompiler.Compile<TDelegate>(@this, returnType, protocol, typesAndNames);

Expand Down Expand Up @@ -104,6 +109,11 @@ namespace AngouriMath.Extensions
/// <#= total #>
/// </code>
/// </example>
/// <remarks>
/// Into a function that returns <see cref="double"/> or <see cref="float"/>, a value that is not real is NaN, and
/// so is every value computed from one, though the expression may be real there: <c>sin(arcsin(x))</c> at 2.
/// <see cref="MathS.Settings.RefuseNonRealCompilation"/> refuses such an expression instead.
/// </remarks>
public static Func<<# for(var t=1;t<=i;t++){ #>TIn<#= t #>, <# } #>TOut> Compile<<# for(var t=1;t<=i;t++){ #>TIn<#= t #>, <# } #>TOut>(this string @this, Variable var1<# for(var t=2; t<=i; t++){ #>, Variable var<#= t #><# } #>)
=> IntoLinqCompiler.Compile<Func<<# for(var t=1;t<=i;t++){ #>TIn<#= t #>, <# } #>TOut>>(@this, typeof(TOut), new(),
new[] { (typeof(TIn1), var1)<# for(var t=2;t<=i;t++){ #>, (typeof(TIn<#= t #>), var<#= t #>) <# } #> });
Expand Down
37 changes: 37 additions & 0 deletions Sources/AngouriMath/Convenience/MathS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6285,6 +6285,43 @@ internal static EDecimal DowncastingTolerance
/// </example>
public static Setting<bool> AllowNewton { get; } = true;

/// <summary>
/// Whether compiling into a function that returns <see cref="double"/> or <see cref="float"/> refuses an
/// expression that is not real wherever it is defined. Off by default.
/// </summary>
/// <remarks>
/// Compiled over the reals, every value the function computes on the way is a real number. So an expression
/// whose value at a real argument is not real, or is real only by way of a value that is not -- <c>sqrt(x)</c>
/// at -4, <c>sin(arcsin(x))</c> at 2 -- evaluates to NaN there, as it does where it has no value at all, as
/// <c>1/x</c> at 0. With this off, that is how such an expression compiles, and a NaN from the compiled
/// function means that the expression has no value there or that its computation left the reals. With it on,
/// an expression that cannot be shown to be real wherever it is defined is refused with
/// <see cref="Core.Exceptions.UncompilableNodeException"/>, and a NaN means only that there is no value. The
/// showing is structural and so conservative: <c>sqrt(x)</c> and <c>ln(x)</c> are refused, though both are
/// real for positive x. Compile over <see cref="System.Numerics.Complex"/> for the value itself.
/// <a href="https://github.com/asc-community/AngouriMath/issues/2286">#2286</a>
/// </remarks>
/// <example>
/// <code>
/// using static AngouriMath.MathS;
///
/// Console.WriteLine("sin(arcsin(x))".Compile&lt;double, double&gt;("x")(2));
///
/// using var _ = Settings.RefuseNonRealCompilation.Set(true);
///
/// Console.WriteLine("x / (1 + x^2)".Compile&lt;double, double&gt;("x")(2));
/// try { "sin(arcsin(x))".Compile&lt;double, double&gt;("x"); }
/// catch (AngouriMath.Core.Exceptions.UncompilableNodeException) { Console.WriteLine("refused"); }
/// </code>
/// Prints
/// <code>
/// NaN
/// 0.4
/// refused
/// </code>
/// </example>
public static Setting<bool> RefuseNonRealCompilation { get; } = false;

/// <summary>
/// Criteria for simplifier so you could control which expressions are considered easier by you.
/// The higher the value - the more complicated an expression is.
Expand Down
Loading
Loading