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 @@ -1034,6 +1034,21 @@ unchanged ([#2256](https://github.com/asc-community/AngouriMath/issues/2256)).
| `"max(x, x)".ToEntity().Simplify()` | `x` | `x provided x in RR` |
| `"max(x, 2)".ToEntity().DomainCondition` | `True` | `x in RR` |

### Over the complex numbers, an equation in and out of a modulus is left unsolved

**Silent: an equation with `x` inside `abs`, `sgn`, `conj`, `Re`, `Im` or `arg` and outside it too is left unsolved
in the complex codomain, the default, where a numerical search answered it.** Newton's method assumes a holomorphic
function, and over the complex numbers `|u| = ±u` only where `u` is real, which a root need not make it, so neither
the search nor a split by the sign finds every root. The search's near-copies of a root came back as roots. Where it
happened to be right, as for `x * abs(x) = 4`, the answer is given up with the rest; over the reals each is still
solved by cases, exactly ([#2139](https://github.com/asc-community/AngouriMath/issues/2139)).

| Input | Was (2.6.0) | Now |
|---|---|---|
| `"x^2/abs(1-x^2) = x".ToEntity().Solve("x")` | `{ 0, 0.618…, 1.618…, 1.618… + 1.7E-9i, 1.618… + 9.9E-8i }` | `{ x : x ^ 2 / abs(1 - x ^ 2) = x }` |
| `"abs(x^2 - 4) = 3x".ToEntity().Solve("x")` | `{ 1, 4, 4.0000000000000062… + 1.6E-7i }` | `{ x : abs(x ^ 2 - 4) = 3 * x }` |
| `"x * abs(x) = 4".ToEntity().Solve("x")` | `{ 2 }` | `{ x : x * abs(x) = 4 }` |

### `conjugate`, `conj` and `arg` are functions

**Not silent: these names followed by a bracket are now the complex conjugate and the principal argument, where they
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,14 @@ bool ProductIsDefinedAt(Entity root)
if (ModulusSolver.IsRealForEveryComplex(expr, x))
return Unsolved(expr, x);

// Over the complex numbers, x inside a modulus or another function that is not
// holomorphic and outside it as well is not the search's either: Newton's method
// assumes a holomorphic function, and its near-copies of a root came back as roots.
// Left unsolved; over the reals it is solved by cases, above.
// https://github.com/asc-community/AngouriMath/issues/2139
if (MathS.Settings.Codomain.Value != Core.Domain.Real && ModulusSolver.IsInsideAndOutsideAFunctionThatIsNotHolomorphic(expr, x))
return Unsolved(expr, x);

// A numerical search that comes back with nothing has not shown there is
// nothing to find: Newton's method is started from finitely many points inside
// a bounded region, so an empty result is a fact about the search rather than
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,28 @@ internal static class ModulusSolver
internal static bool IsRealForEveryComplex(Entity expr, Variable x)
=> expr.ContainsNode(x) && RealWhereverItIs(expr, x);

/// <summary>
/// Whether <paramref name="x"/> is both inside a function that is not holomorphic -- a
/// modulus, a signum, a conjugate, a real or imaginary part, an argument -- and outside every
/// such function somewhere in <paramref name="expr"/>.
/// </summary>
/// <remarks>
/// Newton's method assumes a holomorphic function, and over the complex numbers
/// <c>|u| = +-u</c> only where <c>u</c> is real, which a root need not make it:
/// <c>x^2 + 1 = |x|</c> has roots at <c>+-0.618 i</c>, where <c>|x|</c> is <c>0.618</c>. So
/// neither the search nor a split by the sign finds every root, and the search's near-copies
/// of a root came back as roots: <c>x^2/|1 - x^2| = x</c> had <c>1.618 + 1.7e-9 i</c> beside
/// <c>1.618</c>. https://github.com/asc-community/AngouriMath/issues/2139
/// </remarks>
internal static bool IsInsideAndOutsideAFunctionThatIsNotHolomorphic(Entity expr, Variable x)
=> expr.Nodes.Any(node => IsNotHolomorphic(node) && node.ContainsNode(x)) && IsOutsideThem(expr, x);

private static bool IsNotHolomorphic(Entity node)
=> node is Absf or Signumf or Conjugatef or Ref or Imf or Argf;

private static bool IsOutsideThem(Entity expr, Variable x)
=> expr == x || !IsNotHolomorphic(expr) && expr.DirectChildren.Any(child => IsOutsideThem(child, x));

private static bool RealWhereverItIs(Entity expr, Variable x)
=> expr switch
{
Expand Down
25 changes: 25 additions & 0 deletions Sources/Tests/UnitTests/Algebra/SolveTest/ModulusEquationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,31 @@ public sealed class ModulusEquationTest
public void OverTheComplexNumbersACurveIsNotAListOfPoints(string equation)
=> Assert.IsType<ConditionalSet>(equation.ToEntity().Solve("x"));

/// <summary>
/// <c>x</c> inside a function that is not holomorphic and outside it too: Newton's method
/// assumes a holomorphic function, and over the complex numbers <c>|u| = +-u</c> only where
/// <c>u</c> is real, which a root need not make it, so neither the search nor a split by the
/// sign finds every root. Left unsolved. The search's near-copies of a root came back as
/// roots: <c>x^2/|1 - x^2| = x</c> had <c>1.618 + 1.7e-9 i</c> beside <c>1.618</c>.
/// <a href="https://github.com/asc-community/AngouriMath/issues/2139">#2139</a>
/// </summary>
[Theory]
[InlineData("x^2/abs(1 - x^2) = x")]
[InlineData("abs(x^2 - 4) = 3 x")]
[InlineData("x^2 + 1 = abs(x)")]
[InlineData("x * abs(x) = 4")]
[InlineData("x + sgn(x) = 3")]
[InlineData("x + conj(x) = 2 + i")]
public void OverTheComplexNumbersAnEquationInAndOutOfAModulusIsLeftUnsolved(string equation)
=> Assert.IsType<ConditionalSet>(equation.ToEntity().Solve("x"));

/// <summary>The variable only inside the modulus, or not in one at all, is answered as before.</summary>
[Theory]
[InlineData("abs(x) = 2")]
[InlineData("x^2 = 4")]
public void OverTheComplexNumbersTheOthersAreStillAnswered(string equation)
=> Assert.IsNotType<ConditionalSet>(equation.ToEntity().Solve("x"));

/// <summary>
/// Sullivan and Mackey's Prob 1.5.16, and moduli between which a whole interval solves.
/// </summary>
Expand Down
Loading