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
17 changes: 17 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,23 @@ then had no truth value for. A product or a quotient of two real numbers is now
| `"-x".ToEntity().Compile("x").Call(double.PositiveInfinity)` | `(-Infinity, NaN)` | `(-Infinity, 0)` |
| `"x / 2".ToEntity().Compile<Complex, Complex>("x")(double.PositiveInfinity)` | `(Infinity, NaN)` | `(Infinity, 0)` |

### A compiled function takes the side of its branch cut evaluation takes

**Silent: a compiled logarithm, root or inverse function of a number on its cut is evaluation's value, where it was
the conjugate.** `System.Numerics.Complex` computes `-0` as a part of many real values, `cos(2)` being `(-0.416, -0)`,
and reads it as the lower side of a cut. The library has no signed zero, so a real number is on the upper side. Every
compiled function with a cut now reads a zero part as `+0`: the logarithm, a power that is not whole, the inverse
trigonometric functions, `arg`, the exponential and logarithmic integrals and `lambertw`, in FastExpression and in the
LINQ compiler. A `conj` of a real number, new to compile since 2.6.0, took the same wrong side, as `ln(conj(x))` at
`-2` did ([#2236](https://github.com/asc-community/AngouriMath/issues/2236)). FastExpression's logarithm also kept only
the real part of a complex base, and takes `ln(value) / ln(base)` now
([#2239](https://github.com/asc-community/AngouriMath/issues/2239)).

| Input | Was (2.6.0) | Now |
|---|---|---|
| `"ln(cos(x))".ToEntity().Compile("x").Call(2)` | `(-0.8767171085319084, -3.141592653589793)` | `(-0.8767171085319084, 3.141592653589793)` |
| `"log(x, 2)".ToEntity().Compile("x").Call(new Complex(1, 1))` | `(NaN, NaN)` | `(0.32596797216345985, -0.7387021222729507)` |

### A compiled power of real numbers is the real power, at an infinity and at zero

**Silent: a compiled power over `Complex` is what evaluation gives where it had no value or the wrong one.** In
Expand Down
3 changes: 3 additions & 0 deletions Sources/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -931,3 +931,6 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed

[Tests/UnitTests/Common/RealPowerCompilationTest.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
28 changes: 27 additions & 1 deletion Sources/AngouriMath/Functions/Compilation/ComplexBranches.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,36 @@ internal static class ComplexBranches
/// see https://github.com/asc-community/AngouriMath/issues/115.
/// </remarks>
public static NumericsComplex Arcsin(NumericsComplex a)
=> a.Imaginary == 0 && Math.Abs(a.Real) > 1
=> (a = Unsigned(a)).Imaginary == 0 && Math.Abs(a.Real) > 1
? NumericsComplex.Conjugate(NumericsComplex.Asin(a))
: NumericsComplex.Asin(a);

/// <summary>
/// <paramref name="z"/> with each zero part read as <c>+0</c>. The library has no signed zero, so a real
/// number is on the upper side of every branch cut it lies on; <see cref="System.Numerics.Complex"/>
/// computes <c>-0</c> as a part of many real values, <c>cos(2)</c> and <c>conj(-2)</c> among them, and reads
/// it as the lower side, which made <c>ln(cos(x))</c> at 2 the conjugate of the logarithm. Adding <c>+0</c>
/// turns <c>-0</c> into <c>+0</c> and changes no other number. Taken at the entry of every function with a cut.
/// https://github.com/asc-community/AngouriMath/issues/2236
/// </summary>
public static NumericsComplex Unsigned(NumericsComplex z) => new(z.Real + 0.0, z.Imaginary + 0.0);

/// <summary>
/// The logarithm of <paramref name="value"/> to <paramref name="base"/>, <c>ln(value) / ln(base)</c> for any
/// base, where FastExpression's <c>Complex.Log(value, base.Real)</c> dropped a complex base's imaginary part.
/// https://github.com/asc-community/AngouriMath/issues/2239
/// </summary>
public static NumericsComplex Log(NumericsComplex @base, NumericsComplex value)
=> RealProducts.Quotient(NumericsComplex.Log(Unsigned(value)), NumericsComplex.Log(Unsigned(@base)));

public static NumericsComplex Arccos(NumericsComplex a) => NumericsComplex.Acos(Unsigned(a));

public static NumericsComplex Arctan(NumericsComplex a) => NumericsComplex.Atan(Unsigned(a));

public static NumericsComplex Arccotan(NumericsComplex a) => NumericsComplex.Atan(Unsigned(1 / a));

public static NumericsComplex Arcsecant(NumericsComplex a) => NumericsComplex.Acos(Unsigned(1 / a));

/// <summary>
/// The arccosecant the library evaluates to, which is <see cref="Arcsin"/> of the
/// reciprocal and so lands on the same cuts -- for arccosecant, at the real arguments
Expand Down
25 changes: 14 additions & 11 deletions Sources/AngouriMath/Functions/Compilation/IntoFE/FastExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -250,22 +250,25 @@ public System.Numerics.Complex Substitute(params System.Numerics.Complex[] value
stack.Push(1 / System.Numerics.Complex.Tan(stack.Pop()));
break;
case InstructionType.CALL_LOG:
stack.Push(System.Numerics.Complex.Log(stack.Pop(), stack.Pop().Real));
{
var value = stack.Pop();
stack.Push(Core.Compilation.ComplexBranches.Log(stack.Pop(), value));
}
break;
case InstructionType.CALL_ARCSIN:
stack.Push(Core.Compilation.ComplexBranches.Arcsin(stack.Pop()));
break;
case InstructionType.CALL_ARCCOS:
stack.Push(System.Numerics.Complex.Acos(stack.Pop()));
stack.Push(Core.Compilation.ComplexBranches.Arccos(stack.Pop()));
break;
case InstructionType.CALL_ARCTAN:
stack.Push(System.Numerics.Complex.Atan(stack.Pop()));
stack.Push(Core.Compilation.ComplexBranches.Arctan(stack.Pop()));
break;
case InstructionType.CALL_ARCCOTAN:
stack.Push(System.Numerics.Complex.Atan(1 / stack.Pop()));
stack.Push(Core.Compilation.ComplexBranches.Arccotan(stack.Pop()));
break;
case InstructionType.CALL_ARCSECANT:
stack.Push(System.Numerics.Complex.Acos(1 / stack.Pop()));
stack.Push(Core.Compilation.ComplexBranches.Arcsecant(stack.Pop()));
break;
case InstructionType.CALL_ARCCOSECANT:
stack.Push(Core.Compilation.ComplexBranches.Arccosecant(stack.Pop()));
Expand All @@ -282,22 +285,22 @@ public System.Numerics.Complex Substitute(params System.Numerics.Complex[] value
stack.Push(AngouriMath.Numerics.SpecialFunctions.Erfi(stack.Pop()));
break;
case InstructionType.CALL_EI:
stack.Push(AngouriMath.Numerics.SpecialFunctions.Ei(stack.Pop()));
stack.Push(AngouriMath.Numerics.SpecialFunctions.Ei(Core.Compilation.ComplexBranches.Unsigned(stack.Pop())));
break;
case InstructionType.CALL_LI:
stack.Push(AngouriMath.Numerics.SpecialFunctions.Li(stack.Pop()));
stack.Push(AngouriMath.Numerics.SpecialFunctions.Li(Core.Compilation.ComplexBranches.Unsigned(stack.Pop())));
break;
case InstructionType.CALL_SI:
stack.Push(AngouriMath.Numerics.SpecialFunctions.Si(stack.Pop()));
break;
case InstructionType.CALL_CI:
stack.Push(AngouriMath.Numerics.SpecialFunctions.Ci(stack.Pop()));
stack.Push(AngouriMath.Numerics.SpecialFunctions.Ci(Core.Compilation.ComplexBranches.Unsigned(stack.Pop())));
break;
case InstructionType.CALL_SHI:
stack.Push(AngouriMath.Numerics.SpecialFunctions.Shi(stack.Pop()));
break;
case InstructionType.CALL_CHI:
stack.Push(AngouriMath.Numerics.SpecialFunctions.Chi(stack.Pop()));
stack.Push(AngouriMath.Numerics.SpecialFunctions.Chi(Core.Compilation.ComplexBranches.Unsigned(stack.Pop())));
break;
case InstructionType.CALL_FACTORIAL:
stack.Push(AngouriMath.Numerics.SpecialFunctions.Factorial(stack.Pop()));
Expand All @@ -315,7 +318,7 @@ public System.Numerics.Complex Substitute(params System.Numerics.Complex[] value
stack.Push(AngouriMath.Numerics.ComplexParts.Conj(stack.Pop()));
break;
case InstructionType.CALL_ARG:
stack.Push(AngouriMath.Numerics.ComplexParts.Arg(stack.Pop()));
stack.Push(AngouriMath.Numerics.ComplexParts.Arg(Core.Compilation.ComplexBranches.Unsigned(stack.Pop())));
break;
case InstructionType.CALL_RE:
stack.Push(AngouriMath.Numerics.ComplexParts.Re(stack.Pop()));
Expand Down Expand Up @@ -374,7 +377,7 @@ public System.Numerics.Complex Substitute(params System.Numerics.Complex[] value
stack.Push(AngouriMath.Numerics.CombinatorialFunctions.RisingFactorial(stack.Pop(), stack.Pop()));
break;
case InstructionType.CALL_LAMBERTW:
stack.Push(AngouriMath.Numerics.LambertWFunction.LambertW(stack.Pop(), stack.Pop()));
stack.Push(AngouriMath.Numerics.LambertWFunction.LambertW(stack.Pop(), Core.Compilation.ComplexBranches.Unsigned(stack.Pop())));
break;
case InstructionType.CALL_POLYGAMMA:
stack.Push(AngouriMath.Numerics.PolygammaFunction.Polygamma(stack.Pop(), stack.Pop()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ internal static class MathAllMethods
/* POWERS */

public static System.Numerics.Complex Log(System.Numerics.Complex a, System.Numerics.Complex b)
=> System.Numerics.Complex.Log(b) / System.Numerics.Complex.Log(a);
=> AngouriMath.Core.Compilation.ComplexBranches.Log(a, b);
public static double Log(double a, double b)
=> Math.Log(b, a);
public static float Log(float a, float b)
Expand Down Expand Up @@ -109,7 +109,7 @@ public static BigInteger Asin(BigInteger a)


public static System.Numerics.Complex Acos(System.Numerics.Complex a)
=> System.Numerics.Complex.Acos(a);
=> AngouriMath.Core.Compilation.ComplexBranches.Arccos(a);
public static double Acos(double a)
=> Math.Acos(a);
public static float Acos(float a)
Expand All @@ -123,7 +123,7 @@ public static BigInteger Acos(BigInteger a)


public static System.Numerics.Complex Atan(System.Numerics.Complex a)
=> System.Numerics.Complex.Atan(a);
=> AngouriMath.Core.Compilation.ComplexBranches.Arctan(a);
public static double Atan(double a)
=> Math.Atan(a);
public static float Atan(float a)
Expand Down Expand Up @@ -185,7 +185,7 @@ public static BigInteger Csc(BigInteger a)


public static System.Numerics.Complex Acot(System.Numerics.Complex a)
=> System.Numerics.Complex.Atan(1 / a);
=> AngouriMath.Core.Compilation.ComplexBranches.Arccotan(a);
public static double Acot(double a)
=> Math.Atan(1 / a);
public static float Acot(float a)
Expand All @@ -199,7 +199,7 @@ public static BigInteger Acot(BigInteger a)


public static System.Numerics.Complex Asec(System.Numerics.Complex a)
=> System.Numerics.Complex.Acos(1 / a);
=> AngouriMath.Core.Compilation.ComplexBranches.Arcsecant(a);
public static double Asec(double a)
=> Math.Acos(1 / a);
public static float Asec(float a)
Expand Down Expand Up @@ -299,36 +299,36 @@ public static System.Numerics.Complex Round(System.Numerics.Complex a)

// The special functions, in double precision as the kernels define them, and the factorial,
// the gamma function one along. https://github.com/asc-community/AngouriMath/issues/1607
public static System.Numerics.Complex Erf(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Erf(a);
public static System.Numerics.Complex Erf(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Erf(AngouriMath.Core.Compilation.ComplexBranches.Unsigned(a));
public static double Erf(double a) => AngouriMath.Numerics.SpecialFunctions.Erf(a);
public static System.Numerics.Complex Erfc(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Erfc(a);
public static System.Numerics.Complex Erfc(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Erfc(AngouriMath.Core.Compilation.ComplexBranches.Unsigned(a));
public static double Erfc(double a) => AngouriMath.Numerics.SpecialFunctions.Erfc(a);
public static System.Numerics.Complex Erfi(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Erfi(a);
public static System.Numerics.Complex Erfi(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Erfi(AngouriMath.Core.Compilation.ComplexBranches.Unsigned(a));
public static double Erfi(double a) => AngouriMath.Numerics.SpecialFunctions.Erfi(a);
public static System.Numerics.Complex Ei(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Ei(a);
public static System.Numerics.Complex Ei(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Ei(AngouriMath.Core.Compilation.ComplexBranches.Unsigned(a));
public static double Ei(double a) => AngouriMath.Numerics.SpecialFunctions.Ei(a);
public static System.Numerics.Complex Li(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Li(a);
public static System.Numerics.Complex Li(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Li(AngouriMath.Core.Compilation.ComplexBranches.Unsigned(a));
public static double Li(double a) => AngouriMath.Numerics.SpecialFunctions.Li(a);
public static System.Numerics.Complex Si(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Si(a);
public static System.Numerics.Complex Si(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Si(AngouriMath.Core.Compilation.ComplexBranches.Unsigned(a));
public static double Si(double a) => AngouriMath.Numerics.SpecialFunctions.Si(a);
public static System.Numerics.Complex Ci(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Ci(a);
public static System.Numerics.Complex Ci(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Ci(AngouriMath.Core.Compilation.ComplexBranches.Unsigned(a));
public static double Ci(double a) => AngouriMath.Numerics.SpecialFunctions.Ci(a);
public static System.Numerics.Complex Shi(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Shi(a);
public static System.Numerics.Complex Shi(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Shi(AngouriMath.Core.Compilation.ComplexBranches.Unsigned(a));
public static double Shi(double a) => AngouriMath.Numerics.SpecialFunctions.Shi(a);
public static System.Numerics.Complex Chi(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Chi(a);
public static System.Numerics.Complex Chi(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Chi(AngouriMath.Core.Compilation.ComplexBranches.Unsigned(a));
public static double Chi(double a) => AngouriMath.Numerics.SpecialFunctions.Chi(a);
public static System.Numerics.Complex Factorial(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Factorial(a);
public static System.Numerics.Complex Factorial(System.Numerics.Complex a) => AngouriMath.Numerics.SpecialFunctions.Factorial(AngouriMath.Core.Compilation.ComplexBranches.Unsigned(a));
public static double Factorial(double a) => AngouriMath.Numerics.SpecialFunctions.Factorial(a);

// The conjugate, the principal argument and the real and the imaginary parts.
// https://github.com/asc-community/AngouriMath/issues/2195
public static System.Numerics.Complex Conj(System.Numerics.Complex a) => AngouriMath.Numerics.ComplexParts.Conj(a);
public static System.Numerics.Complex Conj(System.Numerics.Complex a) => AngouriMath.Numerics.ComplexParts.Conj(AngouriMath.Core.Compilation.ComplexBranches.Unsigned(a));
public static double Conj(double a) => AngouriMath.Numerics.ComplexParts.Conj(a);
public static System.Numerics.Complex Arg(System.Numerics.Complex a) => AngouriMath.Numerics.ComplexParts.Arg(a);
public static System.Numerics.Complex Arg(System.Numerics.Complex a) => AngouriMath.Numerics.ComplexParts.Arg(AngouriMath.Core.Compilation.ComplexBranches.Unsigned(a));
public static double Arg(double a) => AngouriMath.Numerics.ComplexParts.Arg(a);
public static System.Numerics.Complex Re(System.Numerics.Complex a) => AngouriMath.Numerics.ComplexParts.Re(a);
public static System.Numerics.Complex Re(System.Numerics.Complex a) => AngouriMath.Numerics.ComplexParts.Re(AngouriMath.Core.Compilation.ComplexBranches.Unsigned(a));
public static double Re(double a) => AngouriMath.Numerics.ComplexParts.Re(a);
public static System.Numerics.Complex Im(System.Numerics.Complex a) => AngouriMath.Numerics.ComplexParts.Im(a);
public static System.Numerics.Complex Im(System.Numerics.Complex a) => AngouriMath.Numerics.ComplexParts.Im(AngouriMath.Core.Compilation.ComplexBranches.Unsigned(a));
public static double Im(double a) => AngouriMath.Numerics.ComplexParts.Im(a);

// The binomial coefficient, the factorial powers and the double factorial, on the interpreter's
Expand All @@ -346,7 +346,7 @@ public static System.Numerics.Complex Round(System.Numerics.Complex a)

// The Lambert W function on the branch the first argument names.
// https://github.com/asc-community/AngouriMath/issues/2195
public static System.Numerics.Complex LambertW(System.Numerics.Complex a, System.Numerics.Complex b) => AngouriMath.Numerics.LambertWFunction.LambertW(a, b);
public static System.Numerics.Complex LambertW(System.Numerics.Complex a, System.Numerics.Complex b) => AngouriMath.Numerics.LambertWFunction.LambertW(a, AngouriMath.Core.Compilation.ComplexBranches.Unsigned(b));
public static double LambertW(double a, double b) => AngouriMath.Numerics.LambertWFunction.LambertW(a, b);

// The polygamma functions of a whole order, and the harmonic numbers.
Expand Down
Loading
Loading