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
6 changes: 5 additions & 1 deletion BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ one inside another was answered with the empty set, a claim that it has no solut
**Silent: the same roots, written as what they are.** The quadratic formula wrote `sqrt(b^2 - 4ac)` whatever the
discriminant was, so a symbolic quadratic with a square discriminant came back with the square under the root.
Either square root gives the same pair of roots, so where the coefficients are polynomials and the discriminant is
a rational number times the square of one, the roots are written with that polynomial and reduced over `2a`
a rational number times the square of one, the roots are written with that polynomial and reduced over `2a`. So is
the square root the solver takes after solving for `x^2`, where an equation has no term in `x`
([#2140](https://github.com/asc-community/AngouriMath/issues/2140)).

| Input | Was (2.6.0) | Now |
Expand All @@ -71,6 +72,9 @@ a rational number times the square of one, the roots are written with that polyn
| `"x^2 - (p + q)*x + p*q = 0".Solve("x")` | `{ (p + q - sqrt((-(p + q)) ^ 2 - 4 * p * q)) / 2, … }` | `{ q, p }` |
| `"x^2 - 2*a*x + a^2 - 2 = 0".Solve("x")` | `{ (-(-2) * a - sqrt(((-2) * a) ^ 2 - 4 * (a ^ 2 + -2))) / 2, … }` | `{ a - sqrt(2), a + sqrt(2) }` |
| `"2*x^2 - 2*(p + q)*x + p^2 + q^2 = 0".Solve("x")` | `{ (-(-2) * (p + q) - sqrt(((-2) * (p + q)) ^ 2 - 8 * (p ^ 2 + q ^ 2))) / 4, … }` | `{ 1/2 * p + 1/2 * q - i * (1/2 * p - 1/2 * q), … }` |
| `"x^2 = (a + b)^2".Solve("x")` | `{ sqrt((a + b) ^ 2), -sqrt((a + b) ^ 2) }` | `{ -a - b, a + b }` |
| `"x^4 - (a^2 + b^2)*x^2 + a^2*b^2 = 0".Solve("x")` | `{ sqrt((a ^ 2 + b ^ 2 - sqrt((-(a ^ 2 + b ^ 2)) ^ 2 - 4 * a ^ 2 * b ^ 2)) / 2), … }` | `{ -b, b, -a, a }` |
| `"sin(x)^2 = (a + 1)^2".Solve("x")` | `{ arcsin(sqrt((a + 1) ^ 2)) + 2 * pi * n_1, … }` | `{ arcsin(-a - 1) + 2 * pi * n_1, … }` |

### A system with a product equation is solved branch by branch

Expand Down
4 changes: 2 additions & 2 deletions Sources/AngouriMath/Core/Entity/Omni/Entity.Matrix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,8 @@ public Entity AsScalar()
/// </summary>
/// <remarks>
/// The characteristic polynomial divides by nothing, so its roots carry no condition an
/// elimination's pivots would bring: <c>[[0, J], [J, 0]]</c> has <c>sqrt(J^2)</c> and
/// <c>-sqrt(J^2)</c>, which hold at <c>J = 0</c> too. Up to the fourth degree the roots are
/// elimination's pivots would bring: <c>[[0, J], [J, 0]]</c> has <c>J</c> and <c>-J</c>,
/// which hold at <c>J = 0</c> too. Up to the fourth degree the roots are
/// in radicals, and past it where the polynomial factors; otherwise the set is the one the
/// equation solver leaves.
/// <a href="https://github.com/asc-community/AngouriMath/issues/1676">#1676</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -400,8 +400,14 @@ Entity GetMonomialByPower(EInteger power) =>
} is { } resConcat
? !gcdPower.Equals(EInteger.One)
// if we had x^6 + x^3 + 1, we replace it with x^2 + x + 1 and find all cubic roots of the final answer
? res.Concat(resConcat).SelectMany(_ => gcdPowerRoots,
(root, coef) => coef * MathS.Pow(root, Rational.Create(1, gcdPower)))
// x^2 = y is a quadratic in x, so a y that is a rational number times a square has
// its two roots written without the root, as the formula's are: x^2 = (a + b)^2 was
// { sqrt((a + b)^2), -sqrt((a + b)^2) } and is { -a - b, a + b }.
// https://github.com/asc-community/AngouriMath/issues/2140
? res.Concat(resConcat).SelectMany(root =>
gcdPower.Equals(EInteger.FromInt32(2)) && RootsOverASquareDiscriminant(1, 0, -root) is { } exact
? exact
: gcdPowerRoots.Select(coef => coef * MathS.Pow(root, Rational.Create(1, gcdPower))))
: res.Concat(resConcat)
: null;
}
Expand Down
4 changes: 3 additions & 1 deletion Sources/Tests/UnitTests/Algebra/EigenvaluesTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ public void ARepeatedRootIsOneMember()
public void OfASymbolicMatrixCarryNoPivotCondition()
{
var eigenvalues = MathS.Matrix(new Entity[,] { { 0, "J" }, { "J", 0 } }).Eigenvalues;
Assert.Equal(new FiniteSet("sqrt(J^2)", "-sqrt(J^2)"), eigenvalues);
// lambda^2 = J^2 is rooted exactly, so these are J and -J rather than the square under
// a root. https://github.com/asc-community/AngouriMath/issues/2140
Assert.Equal(new FiniteSet("-J", "J"), eigenvalues);
Assert.DoesNotContain("provided", eigenvalues!.Stringize());
}

Expand Down
19 changes: 19 additions & 0 deletions Sources/Tests/UnitTests/Algebra/SquareDiscriminantTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,24 @@ public void OnlyTheRootOfTheNumberIsLeft(string equation, string first, string s
Assert.Contains(roots, root => Equal(root, second.ToEntity()));
Assert.DoesNotContain(roots, root => root.Nodes.Any(node => node is Powf(var radicand, Rational { Denominator: var d }) && d == 2 && radicand is not Number));
}

/// <summary>
/// With no term in <c>x</c> the solver takes <c>x^2</c> as its unknown and the square
/// root after, and that root was left over the square too: <c>x^2 = (a + b)^2</c> was
/// <c>{ sqrt((a + b)^2), -sqrt((a + b)^2) }</c>.
/// </summary>
[Theory]
[InlineData("x^2 = (a + b)^2", new[] { "a + b", "-a - b" })]
[InlineData("x^2 + (p - q)^2/4 = 0", new[] { "i*(p - q)/2", "-i*(p - q)/2" })]
[InlineData("x^2 = 2*a^2", new[] { "sqrt(2)*a", "-sqrt(2)*a" })]
[InlineData("x^4 - (a^2 + b^2)*x^2 + a^2*b^2 = 0", new[] { "a", "-a", "b", "-b" })]
public void APowerOfTwoAgainstASquareIsRootedExactly(string equation, string[] expected)
{
var roots = Roots(equation, "x");
Assert.Equal(expected.Length, roots.Length);
foreach (var root in expected)
Assert.Contains(roots, found => Equal(found, root.ToEntity()));
Assert.DoesNotContain(roots, root => root.Nodes.Any(node => node is Powf(var radicand, Rational { Denominator: var d }) && d == 2 && radicand is not Number));
}
}
}
Loading