Skip to content

Commit a0373a4

Browse files
A negative whole power is inverted to its roots, not to none (#2154)
Inverting x^n = v for a whole n multiplies an n-th root of v by each root of unity of order n, which GetAllRootsOf1 lists by counting up to n. For a negative n it counted nothing, so the inversion found no roots, and an equation that reached one inside another was answered with the empty set, a claim that it has none: 1/(1 + x^(-2)) = 1/2 came back { } where its roots are 1 and -1, and 1/(1 + x^(-2)) = y came back { } for every y. x^(-2) = 4 on its own was answered correctly by another route. x^n = v for a negative n has the roots of x^|n| = 1/v, and an n-th root of v times a root of unity of order |n| is each of them, so the roots of unity are now counted to |n|. A negative power is never zero, so x^(-2) = 0 is still answered with none, now on purpose; x^0 is not inverted at all. Measured on 2.6.0 and on this branch, in BREAKING-CHANGES.md. Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 725481c commit a0373a4

4 files changed

Lines changed: 94 additions & 1 deletion

File tree

‎BREAKING-CHANGES.md‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,21 @@ A not-equal written `<>` is read as `not (a = b)`, and printed that way.
4242

4343
## Unreleased — since 2.6.0
4444

45+
### An equation through a negative whole power is not answered with no roots
46+
47+
**Silent: an empty answer becomes the roots.** Inverting `x^n = v` for a negative whole `n` multiplied by the roots
48+
of unity of order `n`, and there are none of order `-2`, so the inversion found no roots, and an equation that reached
49+
one inside another was answered with the empty set, a claim that it has no solution. `x^n = v` has the roots of
50+
`x^|n| = 1/v`, and they are found now. `x^(-2) = 4` on its own was already answered by another route
51+
([#2144](https://github.com/asc-community/AngouriMath/issues/2144)).
52+
53+
| Input | Was (2.6.0) | Now |
54+
|---|---|---|
55+
| `"1/(1 + x^(-2)) = 1/2".Solve("x")` | `{ }` | `{ 1, -1 }` |
56+
| `"2/(1 + x^(-2)) = 1".Solve("x")` | `{ }` | `{ 1, -1 }` |
57+
| `"1/(2 + p^(-3)) = 1/3".Solve("p")` | `{ }` | `{ 1, -1/2 + i * 1/2 * sqrt(3), -1/2 + i * -1/2 * sqrt(3) }` |
58+
| `"1/(1 + x^(-2)) = y".Solve("x")` | `{ }` | `{ ((-1) / (-y) - 1) ^ (-1/2), -((-1) / (-y) - 1) ^ (-1/2) }` |
59+
4560
### A system with a product equation is solved branch by branch
4661

4762
**Silent: solutions that were missing are answered.** `EquationSystem.Solve` eliminated `x * y = 0` for `y` as `0`,

‎Sources/.editorconfig‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,9 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed
273273
[Tests/UnitTests/Convenience/DocCommentPlacementTest.cs]
274274
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
275275

276+
[Tests/UnitTests/Algebra/SolveTest/NegativePowerInversionTest.cs]
277+
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
278+
276279
[Tests/UnitTests/Common/LambdaArrowSyntaxTest.cs]
277280
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
278281

‎Sources/AngouriMath/Functions/Continuous/Solvers/EquationSolver/InvertNode.Classes.cs‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,19 @@ partial record Powf
103103
if (Base.ContainsNode(x))
104104
{
105105
if (Exponent is Integer { EInteger: var pow })
106-
return Together(Number.GetAllRootsOf1(pow).Select(root => Base.Invert(root * MathS.Pow(value, 1 / Exponent), x)));
106+
{
107+
// x^n = v has |n| roots for a negative n as well, those of x^|n| = 1/v:
108+
// each n-th root of v times a root of unity of order |n|, whose n-th
109+
// power is 1. GetAllRootsOf1 counts up to its argument, so given -2 it
110+
// gave no roots at all, and 1/(1 + x^(-2)) = 1/2 was answered { }. A
111+
// negative power is never zero, so x^(-2) = 0 is the one that has none.
112+
// https://github.com/asc-community/AngouriMath/issues/2144
113+
if (pow.IsZero)
114+
return null;
115+
if (pow.Sign < 0 && value.Evaled is Complex { IsZero: true })
116+
return Enumerable.Empty<Entity>();
117+
return Together(Number.GetAllRootsOf1(pow.Abs()).Select(root => Base.Invert(root * MathS.Pow(value, 1 / Exponent), x)));
118+
}
107119
else
108120
return Base.Invert(MathS.Pow(value, 1 / Exponent), x);
109121
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//
2+
// Copyright (c) 2019-2026 Angouri.
3+
// AngouriMath is licensed under MIT.
4+
// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
5+
// Website: https://am.angouri.org.
6+
//
7+
8+
using AngouriMath;
9+
using AngouriMath.Extensions;
10+
using System.Linq;
11+
using Xunit;
12+
using static AngouriMath.Entity;
13+
using static AngouriMath.Entity.Set;
14+
15+
namespace AngouriMath.Tests.Algebra.SolveTest
16+
{
17+
/// <summary>
18+
/// Inverting a negative whole power found no roots: the roots of unity it multiplies by were
19+
/// counted up to the exponent, and up to -2 is none. So an equation that reaches one inside
20+
/// another, as <c>1/(1 + x^(-2)) = 1/2</c> does, was answered with the empty set, a claim that
21+
/// it has no roots, where it has 1 and -1.
22+
/// <a href="https://github.com/asc-community/AngouriMath/issues/2144">#2144</a>
23+
/// </summary>
24+
[Trait("Area", "Algebra")]
25+
public sealed class NegativePowerInversionTest
26+
{
27+
private static Set Solve(string statement, string x = "x") => statement.ToEntity().Solve(x);
28+
29+
private static Entity IsIn(string value, Set set) => value.ToEntity().In(set).Simplify();
30+
31+
[Theory]
32+
[InlineData("1/(1 + x^(-2)) = 1/2", "1", "-1", "2")]
33+
[InlineData("1/(2 + x^(-3)) = 1/3", "1", "-1/2 + 1/2 * sqrt(3) * i", "-1")]
34+
[InlineData("2/(1 + x^(-2)) = 1", "1", "-1", "0")]
35+
[InlineData("1/(3 + (x + 1)^(-2)) = 1/4", "0", "-2", "1")]
36+
public void EveryRootIsFound(string equation, string root, string otherRoot, string nonRoot)
37+
{
38+
var solved = Solve(equation);
39+
Assert.Equal(Entity.Boolean.True, IsIn(root, solved));
40+
Assert.Equal(Entity.Boolean.True, IsIn(otherRoot, solved));
41+
Assert.Equal(Entity.Boolean.False, IsIn(nonRoot, solved));
42+
}
43+
44+
/// <summary>
45+
/// With a symbol on the right the answer is the two roots in terms of it, which at
46+
/// y = 1/2 are the roots above.
47+
/// </summary>
48+
[Fact]
49+
public void TheRootsInASymbolAreTheRootsAtEachValue()
50+
{
51+
var solved = Assert.IsType<FiniteSet>(Solve("1/(1 + x^(-2)) = y"));
52+
Assert.Equal(2, solved.Count);
53+
var atAHalf = solved.Select(root => root.Substitute("y", "1/2".ToEntity()).Simplify()).ToList();
54+
Assert.Contains(atAHalf, root => root == 1);
55+
Assert.Contains(atAHalf, root => root == -1);
56+
}
57+
58+
/// <summary>A negative power is never zero, so this one does have none.</summary>
59+
[Fact]
60+
public void ANegativePowerIsNeverZero()
61+
=> Assert.Equal(Set.Empty, Solve("1/(1 + x^(-2)) = 1"));
62+
}
63+
}

0 commit comments

Comments
 (0)