diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index a3ed4d206..fcfe5656a 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -77,6 +77,18 @@ its rows in the order it finds them, which can differ from the eliminator's | `MathS.Equations("x*y", "x + y^2 - 1").Solve("x", "y")` | `[[1, 0]]` | `[[1, 0], [0, 1], [0, -1]]` | | `MathS.Equations("x*y + 3*x - 5*y - 15", "x^2 - 3*x - 4*y - 10").Solve("x", "y")` | `[[1, -3], [2, -3]]` | `[[5, 0], [1, -3], [2, -3]]` | +### A lambda applied to a vector multiplies by it as a vector + +**Silent: a matrix of vectors becomes the product.** Applying a lambda simplified its body first, and a simplified +body treats its parameter as a number: `[[0, -1], [1, 0]] * u` became `[[0, -u], [u, 0]]`, and the vector then put in +for `u` made a matrix of vectors. A lambda written in place is now applied before its body is simplified +([#2132](https://github.com/asc-community/AngouriMath/issues/2132)). `Evaled` and `Simplify()` agree. + +| Input | Was (2.6.0) | Now | +|---|---|---| +| `apply(lambda(u, [[0, -1], [1, 0]] * u), [p, q])` | `[[0, [-p, -q]], [[p, q], 0]]` | `[-q, p]` | +| `apply(lambda(u, [1, 1]T * u), [p, q])` | `[[[p, q], [p, q]]]` | `p + q` | + ### A square system is not answered with rows that are no solution **Silent: a row that is no solution is gone, and a system with a row that fails for some values is `null`.** diff --git a/Sources/AngouriMath/Functions/Evaluation/Evaluation.Omni/Evaluation.Omni.Classes.cs b/Sources/AngouriMath/Functions/Evaluation/Evaluation.Omni/Evaluation.Omni.Classes.cs index 1a37d9ec9..628fdcced 100644 --- a/Sources/AngouriMath/Functions/Evaluation/Evaluation.Omni/Evaluation.Omni.Classes.cs +++ b/Sources/AngouriMath/Functions/Evaluation/Evaluation.Omni/Evaluation.Omni.Classes.cs @@ -547,6 +547,16 @@ private static Entity ApplyOthersIfNeeded(Entity outer, LList arguments) /// protected override Entity InnerSimplify(bool isExact) + // A lambda written in place is applied to its argument before its body is + // simplified. Simplified first, the body treats its parameter as a number: + // [[0, -1], [1, 0]] * u becomes [[0, -u], [u, 0]], and the vector [p, q] put in + // for u afterwards made a matrix of vectors where the product is [-q, p]. + // https://github.com/asc-community/AngouriMath/issues/2132 + => Expression is Lambda(var parameter, var body) && Arguments is (var first, var rest) + ? ApplyOthersIfNeeded(body.Substitute(parameter, first.InnerSimplified(isExact)), rest).InnerSimplified(isExact) + : ApplyingTheSimplified(isExact); + + private Entity ApplyingTheSimplified(bool isExact) => ((Expression.InnerSimplified(isExact), Arguments.Map(arg => arg.InnerSimplified(isExact))) switch { (var identifier, LEmpty) => identifier, diff --git a/Sources/Tests/UnitTests/Common/LambdaCalculusTest.cs b/Sources/Tests/UnitTests/Common/LambdaCalculusTest.cs index 39a08a572..74f697fdf 100644 --- a/Sources/Tests/UnitTests/Common/LambdaCalculusTest.cs +++ b/Sources/Tests/UnitTests/Common/LambdaCalculusTest.cs @@ -223,5 +223,19 @@ [Fact] public void EtaReduction8() ) )".Simplify() ); + + // A lambda is applied to its argument before its body is simplified: simplified first, + // the body treats its parameter as a number, so [[0, -1], [1, 0]] * u became + // [[0, -u], [u, 0]] and the vector put in for u made a matrix of vectors. + // https://github.com/asc-community/AngouriMath/issues/2132 + [Theory] + [InlineData("apply(lambda(u, [[0, -1], [1, 0]] * u), [p, q])", "[-q, p]")] + [InlineData("apply(lambda(u, [1, 1]T * u), [p, q])", "p + q")] + [InlineData("apply(lambda(u, v, [u, v]T * [1, 2]), 3, 4)", "11")] + public void ALambdaIsAppliedToAVectorAsAVector(string application, string expected) + { + Assert.Equal(expected.ToEntity(), application.ToEntity().InnerSimplified); + Assert.Equal(expected.ToEntity(), application.ToEntity().Simplify()); + } } }