From 43c83b90f88d5f2510464f2563280087087aadc7 Mon Sep 17 00:00:00 2001 From: "Sasha.potapov2002" Date: Mon, 5 Oct 2020 02:32:46 +0300 Subject: [PATCH 01/12] started working on indefinite integration rules --- .../Continuous/Algebra/Integration.cs | 2 +- .../Integration/IndefiniteIntegralSolver.cs | 51 +++++++++++++++++++ .../Algebra/Integration/Integration.cs | 23 +++++++++ Samples/Samples/Program.cs | 10 ++-- 4 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 AngouriMath/Functions/Continuous/Algebra/Integration/IndefiniteIntegralSolver.cs create mode 100644 AngouriMath/Functions/Continuous/Algebra/Integration/Integration.cs diff --git a/AngouriMath/Functions/Continuous/Algebra/Integration.cs b/AngouriMath/Functions/Continuous/Algebra/Integration.cs index 7a2740622..d81530cf2 100644 --- a/AngouriMath/Functions/Continuous/Algebra/Integration.cs +++ b/AngouriMath/Functions/Continuous/Algebra/Integration.cs @@ -53,7 +53,7 @@ public Complex DefiniteIntegral(Variable x, (EDecimal Re, EDecimal Im) from, (ED } namespace AngouriMath.Functions.Algebra { - internal static class Integration + public static partial class Integration { /// Numerical definite integration, see more in internal static Complex Integrate(Entity func, Entity.Variable x, (EDecimal Re, EDecimal Im) from, (EDecimal Re, EDecimal Im) to, int stepCount) diff --git a/AngouriMath/Functions/Continuous/Algebra/Integration/IndefiniteIntegralSolver.cs b/AngouriMath/Functions/Continuous/Algebra/Integration/IndefiniteIntegralSolver.cs new file mode 100644 index 000000000..da52c8c07 --- /dev/null +++ b/AngouriMath/Functions/Continuous/Algebra/Integration/IndefiniteIntegralSolver.cs @@ -0,0 +1,51 @@ +using PeterO.Numbers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AngouriMath.Functions.Algebra +{ + static class IndefiniteIntegralSolver + { + internal static Entity? SolveBySplittingSum(Entity expr, Entity.Variable x) + { + var splitted = TreeAnalyzer.GatherLinearChildrenOverSumAndExpand(expr, e => e.Contains(x)); + if (splitted is null || splitted.Count < 2) return null; // nothing to do, let other solvers do the work + splitted[0] = Integration.ComputeIndefiniteIntegral(splitted[0], x); // base case for aggregate + var result = splitted.Aggregate((e1, e2) => e1 + Integration.ComputeIndefiniteIntegral(e2, x)); + return result; + } + + internal static Entity? SolveAsPolynomialTerm(Entity expr, Entity.Variable x) => expr switch + { + Entity.Mulf(var m1, var m2) => + !m1.Contains(x) ? + m1 * Integration.ComputeIndefiniteIntegral(m2, x) : + !m2.Contains(x) ? + m2 * Integration.ComputeIndefiniteIntegral(m1, x) : + null, + + Entity.Divf(var div, var over) => + !div.Contains(x) ? + over is Entity.Powf(var @base, var power) ? + div * Integration.ComputeIndefiniteIntegral(MathS.Pow(@base, -power), x) : + div * Integration.ComputeIndefiniteIntegral(MathS.Pow(over, -1), x) : + !over.Contains(x) ? + Integration.ComputeIndefiniteIntegral(div, x) / over : + null, + + Entity.Powf(var @base, var power) => + !power.Contains(x) && @base == x ? + power == -1 ? + MathS.Ln(@base) : // TODO: here should be ln(abs(x)) but for now I left it as is + MathS.Pow(x, power + 1) / (power + 1) : + null, + + Entity.Variable(var v) => + v == x ? MathS.Pow(x, 2) / 2 : v * x, + + _ => null + }; + } +} diff --git a/AngouriMath/Functions/Continuous/Algebra/Integration/Integration.cs b/AngouriMath/Functions/Continuous/Algebra/Integration/Integration.cs new file mode 100644 index 000000000..ea8c5c47d --- /dev/null +++ b/AngouriMath/Functions/Continuous/Algebra/Integration/Integration.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace AngouriMath.Functions.Algebra +{ + public static partial class Integration + { + public static Entity ComputeIndefiniteIntegral(Entity expr, Entity.Variable x) + { + if (!expr.Contains(x)) return expr * x; // base case, handle here + + Entity? answer = null; + answer = IndefiniteIntegralSolver.SolveBySplittingSum(expr, x); + if (answer is { }) return answer; + + answer = IndefiniteIntegralSolver.SolveAsPolynomialTerm(expr, x); + if (answer is { }) return answer; + + return new Entity.Integralf(expr, x, 1); // return as integral if nothing can be done with expression + } + } +} diff --git a/Samples/Samples/Program.cs b/Samples/Samples/Program.cs index e08fa8409..5d800c367 100644 --- a/Samples/Samples/Program.cs +++ b/Samples/Samples/Program.cs @@ -6,6 +6,7 @@ using static AngouriMath.Entity.Boolean; using static AngouriMath.MathS; using AngouriMath.Extensions; +using AngouriMath.Functions.Algebra; namespace Samples { @@ -13,12 +14,9 @@ class Program { static void Main(string[] _) { - var set = "x2 = 16 and x > 0 or x = a".Solve("x"); - Console.WriteLine(set); - //if (set.IsFiniteSet(out var roots)) - // foreach (var root in roots) - // //Console.WriteLine(root.InnerSimplifyWithCheck()); - // Console.WriteLine(root.InnerSimplifyWithCheck()); + var integ = "x4 + 1/(x2) - b*x^a + c"; + var ans = Integration.ComputeIndefiniteIntegral(integ, "x"); + Console.WriteLine(ans.Simplify()); } } } \ No newline at end of file From f6d969719cca6bbe26e0fbf95312aefbec48897c Mon Sep 17 00:00:00 2001 From: "Sasha.potapov2002" Date: Mon, 5 Oct 2020 16:13:00 +0300 Subject: [PATCH 02/12] added some standard integral patterns, integration by parts for polynomials --- .../Integration/IndefiniteIntegralSolver.cs | 31 ++++++++++- .../Algebra/Integration/IntegralPatterns.cs | 51 +++++++++++++++++++ .../Algebra/Integration/Integration.cs | 7 +++ Samples/Samples/Program.cs | 2 +- 4 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs diff --git a/AngouriMath/Functions/Continuous/Algebra/Integration/IndefiniteIntegralSolver.cs b/AngouriMath/Functions/Continuous/Algebra/Integration/IndefiniteIntegralSolver.cs index da52c8c07..59001af25 100644 --- a/AngouriMath/Functions/Continuous/Algebra/Integration/IndefiniteIntegralSolver.cs +++ b/AngouriMath/Functions/Continuous/Algebra/Integration/IndefiniteIntegralSolver.cs @@ -1,4 +1,5 @@ -using PeterO.Numbers; +using AngouriMath.Functions.Algebra.AnalyticalSolving; +using PeterO.Numbers; using System; using System.Collections.Generic; using System.Linq; @@ -47,5 +48,33 @@ over is Entity.Powf(var @base, var power) ? _ => null }; + + internal static Entity? SolveIntegratingByParts(Entity expr, Entity.Variable x) + { + static Entity? IntegrateByParts(Entity v, Entity u, Entity.Variable x, int currentRecursion = 0) + { + if (v == 0) return 0; + if (currentRecursion == MathS.Settings.MaxExpansionTermCount) return null; + + var integral = Integration.ComputeIndefiniteIntegral(u, x); + var differential = v.Derive(x).InnerSimplify(); + var result = IntegrateByParts(differential, integral, x, currentRecursion + 1); + return (result is null) ? null : v * integral - result; + } + + if (expr is Entity.Mulf(var f, var g)) + { + if (MathS.TryPolynomial(f, x, out var fPoly)) + { + return IntegrateByParts(fPoly, g, x); + } + if (MathS.TryPolynomial(g, x, out var gPoly)) + { + return IntegrateByParts(gPoly, f, x); + } + else return null; + } + else return null; + } } } diff --git a/AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs b/AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs new file mode 100644 index 000000000..90515523e --- /dev/null +++ b/AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs @@ -0,0 +1,51 @@ +using AngouriMath.Functions.Algebra; +using Antlr4.Runtime.Misc; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace AngouriMath.Functions.Algebra +{ + internal static class IntegralPatterns + { + internal static Entity? TryStandardIntegrals(Entity expr, Entity.Variable x) => expr switch + { + Entity.Sinf(var arg) => + arg == x ? + -MathS.Cos(x) : + arg is Entity.Mulf(var a, var y) && !a.Contains(x) && y == x ? + -MathS.Cos(x) / a : + null, + + Entity.Cosf(var arg) => + arg == x ? + MathS.Sin(x) : + arg is Entity.Mulf(var a, var y) && !a.Contains(x) && y == x ? + MathS.Sin(x) / a : + null, + + Entity.Tanf(var arg) => + arg == x || arg == -x ? + -MathS.Ln(MathS.Cos(arg)) : + null, + + Entity.Cotanf(var arg) => + arg == x || arg == -x ? + MathS.Ln(MathS.Sin(arg)) : + null, + + Entity.Logf(var @base, var arg) => + !@base.Contains(x) && (arg == x || arg == -x) ? + (arg * MathS.Ln(arg) - arg) / MathS.Ln(@base) : + null, + + Entity.Powf(var @base, var power) => + !@base.Contains(x) ? + MathS.Pow(@base, power) / MathS.Ln(@base) : + null, + + _ => null + }; + } +} diff --git a/AngouriMath/Functions/Continuous/Algebra/Integration/Integration.cs b/AngouriMath/Functions/Continuous/Algebra/Integration/Integration.cs index ea8c5c47d..d2b0ec9e1 100644 --- a/AngouriMath/Functions/Continuous/Algebra/Integration/Integration.cs +++ b/AngouriMath/Functions/Continuous/Algebra/Integration/Integration.cs @@ -11,12 +11,19 @@ public static Entity ComputeIndefiniteIntegral(Entity expr, Entity.Variable x) if (!expr.Contains(x)) return expr * x; // base case, handle here Entity? answer = null; + + answer = IntegralPatterns.TryStandardIntegrals(expr, x); + if (answer is { }) return answer; + answer = IndefiniteIntegralSolver.SolveBySplittingSum(expr, x); if (answer is { }) return answer; answer = IndefiniteIntegralSolver.SolveAsPolynomialTerm(expr, x); if (answer is { }) return answer; + answer = IndefiniteIntegralSolver.SolveIntegratingByParts(expr, x); + if (answer is { }) return answer; + return new Entity.Integralf(expr, x, 1); // return as integral if nothing can be done with expression } } diff --git a/Samples/Samples/Program.cs b/Samples/Samples/Program.cs index 5d800c367..a3ea43cd8 100644 --- a/Samples/Samples/Program.cs +++ b/Samples/Samples/Program.cs @@ -14,7 +14,7 @@ class Program { static void Main(string[] _) { - var integ = "x4 + 1/(x2) - b*x^a + c"; + var integ = "(x2 + 2x + 3) * sin(x)"; var ans = Integration.ComputeIndefiniteIntegral(integ, "x"); Console.WriteLine(ans.Simplify()); } From 487a9b42c468db94fadeeaed0620bb23191b0a4e Mon Sep 17 00:00:00 2001 From: "Sasha.potapov2002" Date: Mon, 5 Oct 2020 16:14:12 +0300 Subject: [PATCH 03/12] oops, some mistake in sin/cos integral case --- .../Continuous/Algebra/Integration/IntegralPatterns.cs | 8 ++++---- Samples/Samples/Program.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs b/AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs index 90515523e..16c919859 100644 --- a/AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs +++ b/AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs @@ -13,16 +13,16 @@ internal static class IntegralPatterns { Entity.Sinf(var arg) => arg == x ? - -MathS.Cos(x) : + -MathS.Cos(arg) : arg is Entity.Mulf(var a, var y) && !a.Contains(x) && y == x ? - -MathS.Cos(x) / a : + -MathS.Cos(arg) / a : null, Entity.Cosf(var arg) => arg == x ? - MathS.Sin(x) : + MathS.Sin(arg) : arg is Entity.Mulf(var a, var y) && !a.Contains(x) && y == x ? - MathS.Sin(x) / a : + MathS.Sin(arg) / a : null, Entity.Tanf(var arg) => diff --git a/Samples/Samples/Program.cs b/Samples/Samples/Program.cs index a3ea43cd8..bfb541bad 100644 --- a/Samples/Samples/Program.cs +++ b/Samples/Samples/Program.cs @@ -14,7 +14,7 @@ class Program { static void Main(string[] _) { - var integ = "(x2 + 2x + 3) * sin(x)"; + var integ = "sin(a*x)"; var ans = Integration.ComputeIndefiniteIntegral(integ, "x"); Console.WriteLine(ans.Simplify()); } From e3a6e52f07be5d9df6758bfad6b1416ed7640d23 Mon Sep 17 00:00:00 2001 From: "Sasha.potapov2002" Date: Mon, 5 Oct 2020 16:32:59 +0300 Subject: [PATCH 04/12] more cases for standard integrals --- .../Algebra/Integration/IntegralPatterns.cs | 16 ++++++++++++---- .../Evaluation/Evaluation.Continuous.Classes.cs | 11 ++++++++--- Samples/Samples/Program.cs | 2 +- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs b/AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs index 16c919859..85efe42e3 100644 --- a/AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs +++ b/AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs @@ -14,15 +14,23 @@ internal static class IntegralPatterns Entity.Sinf(var arg) => arg == x ? -MathS.Cos(arg) : - arg is Entity.Mulf(var a, var y) && !a.Contains(x) && y == x ? - -MathS.Cos(arg) / a : + arg is Entity.Mulf(var a1, var y1) && !a1.Contains(x) && y1 == x ? + -MathS.Cos(arg) / a1 : + arg is Entity.Mulf(var y2, var a2) && !a2.Contains(x) && y2 == x ? + -MathS.Cos(arg) / a2 : + arg is Entity.Divf(var y3, var a3) && !a3.Contains(x) && y3 == x ? + -MathS.Cos(arg) * a3 : null, Entity.Cosf(var arg) => arg == x ? MathS.Sin(arg) : - arg is Entity.Mulf(var a, var y) && !a.Contains(x) && y == x ? - MathS.Sin(arg) / a : + arg is Entity.Mulf(var a1, var y1) && !a1.Contains(x) && y1 == x ? + MathS.Sin(arg) / a1 : + arg is Entity.Mulf(var y2, var a2) && !a2.Contains(x) && y2 == x ? + MathS.Sin(arg) / a2 : + arg is Entity.Divf(var y3, var a3) && !a3.Contains(x) && y3 == x ? + MathS.Sin(arg) * a3 : null, Entity.Tanf(var arg) => diff --git a/AngouriMath/Functions/Evaluation/Evaluation.Continuous.Classes.cs b/AngouriMath/Functions/Evaluation/Evaluation.Continuous.Classes.cs index 047562afe..a3f46117f 100644 --- a/AngouriMath/Functions/Evaluation/Evaluation.Continuous.Classes.cs +++ b/AngouriMath/Functions/Evaluation/Evaluation.Continuous.Classes.cs @@ -17,6 +17,7 @@ using System; using static AngouriMath.Entity.Number; using AngouriMath.Core; +using AngouriMath.Functions.Algebra; namespace AngouriMath { @@ -333,14 +334,18 @@ public partial record Integralf (var expr, _, Integer(0)) => expr, // TODO: consider Derivative for negative cases (var expr, Variable var, Integer { EInteger: var asInt }) => - throw new NotImplementedException("Integration is not implemented yet"), + asInt.CompareTo(1) == 0 ? + Integration.ComputeIndefiniteIntegral(expr, var) : + throw new NotImplementedException("Integral powers are not implemented yet"), _ => this }; internal override Entity InnerSimplify() => - Var.InnerSimplifyWithCheck() is Variable && Iterations.InnerSimplifyWithCheck() is Integer { EInteger: var asInt } + Var.InnerSimplifyWithCheck() is Variable var && Iterations.InnerSimplifyWithCheck() is Integer { EInteger: var asInt } ? asInt.IsZero ? Expression.InnerSimplifyWithCheck() - : throw new NotImplementedException("Integration is not implemented yet") + : asInt.CompareTo(1) == 0 ? + Integration.ComputeIndefiniteIntegral(Expression, var) : + throw new NotImplementedException("Integral powers are not implemented yet") : this; } public partial record Limitf diff --git a/Samples/Samples/Program.cs b/Samples/Samples/Program.cs index bfb541bad..265030132 100644 --- a/Samples/Samples/Program.cs +++ b/Samples/Samples/Program.cs @@ -14,7 +14,7 @@ class Program { static void Main(string[] _) { - var integ = "sin(a*x)"; + var integ = "ln(x/2)"; var ans = Integration.ComputeIndefiniteIntegral(integ, "x"); Console.WriteLine(ans.Simplify()); } From 45403d0bf44b0ee4e9a03d5aba028993c5945ae8 Mon Sep 17 00:00:00 2001 From: Angourisoft Date: Tue, 6 Oct 2020 21:37:17 +0300 Subject: [PATCH 05/12] Polynomial parser --- .../Evaluation/TreeAnalyzer/LongDivision.cs | 50 +++++----- .../Evaluation/TreeAnalyzer/PolyParser.cs | 97 +++++++++++++++++++ Tests/UnitTests/Core/PolyParser.cs | 97 +++++++++++++++++++ 3 files changed, 220 insertions(+), 24 deletions(-) create mode 100644 AngouriMath/Functions/Evaluation/TreeAnalyzer/PolyParser.cs create mode 100644 Tests/UnitTests/Core/PolyParser.cs diff --git a/AngouriMath/Functions/Evaluation/TreeAnalyzer/LongDivision.cs b/AngouriMath/Functions/Evaluation/TreeAnalyzer/LongDivision.cs index 85bd35253..b3cff51dc 100644 --- a/AngouriMath/Functions/Evaluation/TreeAnalyzer/LongDivision.cs +++ b/AngouriMath/Functions/Evaluation/TreeAnalyzer/LongDivision.cs @@ -23,11 +23,11 @@ namespace AngouriMath.Functions using static Entity.Number; internal static partial class TreeAnalyzer { - class PolynomialInformation + internal sealed class PolynomialInformation { - readonly Dictionary> monoInfo = new(); - readonly Dictionary replacements = new(); - readonly Dictionary revertReplacements = new(); + private readonly Dictionary> monoInfo = new(); + private readonly Dictionary replacements = new(); + private readonly Dictionary revertReplacements = new(); public IReadOnlyDictionary> MonoInfo => monoInfo; public IReadOnlyDictionary Replacements => replacements; public IReadOnlyDictionary RevertReplacements => revertReplacements; @@ -42,32 +42,34 @@ public void AddMonoInfo(Variable variable, Dictionary? powers) if (powers is { }) monoInfo.Add(variable, powers); } } + + internal static PolynomialInformation GatherAllPossiblePolynomials(Entity expr, bool replaceVars) + { + // Init + var res = new PolynomialInformation(); + + if (replaceVars) + { + // Replace all variables we can + foreach (var varMentioned in expr.Vars) + res.AddReplacement(expr.Vars, GetMinimumSubtree(expr, varMentioned)); + expr = expr.Substitute(res.Replacements); + } + + // Gather info about each var as if this var was the only argument of the polynomial P(x) + var children = Sumf.LinearChildren(expr); + foreach (var varMentioned in expr.Vars) + res.AddMonoInfo(varMentioned, Algebra.AnalyticalSolving.PolynomialSolver.GatherMonomialInformation + (children, varMentioned)); + return res; + } + /// /// Divides one polynomial over another one: /// /// internal static (Entity Divided, Entity Remainder)? PolynomialLongDivision(Entity p, Entity q) { - static PolynomialInformation GatherAllPossiblePolynomials(Entity expr, bool replaceVars) - { - // Init - var res = new PolynomialInformation(); - - if (replaceVars) - { - // Replace all variables we can - foreach (var varMentioned in expr.Vars) - res.AddReplacement(expr.Vars, GetMinimumSubtree(expr, varMentioned)); - expr = expr.Substitute(res.Replacements); - } - - // Gather info about each var as if this var was the only argument of the polynomial P(x) - var children = Sumf.LinearChildren(expr); - foreach (var varMentioned in expr.Vars) - res.AddMonoInfo(varMentioned, Algebra.AnalyticalSolving.PolynomialSolver.GatherMonomialInformation - (children, varMentioned)); - return res; - } if (!p.Vars.Any() || !q.Vars.Any()) return null; // There are no variables to find polynomial as diff --git a/AngouriMath/Functions/Evaluation/TreeAnalyzer/PolyParser.cs b/AngouriMath/Functions/Evaluation/TreeAnalyzer/PolyParser.cs new file mode 100644 index 000000000..367e41523 --- /dev/null +++ b/AngouriMath/Functions/Evaluation/TreeAnalyzer/PolyParser.cs @@ -0,0 +1,97 @@ + +/* Copyright (c) 2019-2020 Angourisoft + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using AngouriMath.Core; +using AngouriMath.Core.Exceptions; +using PeterO.Numbers; + +namespace AngouriMath.Functions +{ + using static Entity; + using static Entity.Number; + internal static partial class TreeAnalyzer + { + /// + /// Finds all monomials with respect to the power of + /// + internal static bool TryGetPolynomial(Entity expr, Variable variable, + [NotNullWhen(true)] out Dictionary? dst) + { + var children = Sumf.LinearChildren(expr.Expand()); + dst = Algebra.AnalyticalSolving.PolynomialSolver.GatherMonomialInformation + (children, variable); + return dst is not null; + } + + /// a x + b + internal static bool TryGetPolyLinear(Entity expr, Variable variable, + [NotNullWhen(true)] out Entity? a, + [NotNullWhen(true)] out Entity? b) + { + a = b = null; + if (TryGetPolynomial(expr, variable, out var monoInfo) + && AssertIsValidNormalPoly(monoInfo, 1, out var res)) + { + a = res[1]; + b = res[0]; + return true; + } + return false; + } + + /// a x ^ 2 + b x + c + internal static bool TryGetPolyQuadratic(Entity expr, Variable variable, + [NotNullWhen(true)] out Entity? a, + [NotNullWhen(true)] out Entity? b, + [NotNullWhen(true)] out Entity? c) + { + a = b = c = null; + if (TryGetPolynomial(expr, variable, out var monoInfo) + && AssertIsValidNormalPoly(monoInfo, 2, out var res)) + { + a = res[2]; + b = res[1]; + c = res[0]; + return true; + } + return false; + } + + private static bool AssertIsValidNormalPoly(Dictionary polyInfo, + int maxPower, out Entity[] monoByPower) + { + monoByPower = new Entity[maxPower + 1]; + foreach (var pair in polyInfo) + { + if (!pair.Key.CanFitInInt32()) + return false; + var asInt = pair.Key.ToInt32Unchecked(); + if (asInt < 0) + return false; + if (asInt > maxPower) + return false; + monoByPower[asInt] = pair.Value; + } + for (int i = 0; i < monoByPower.Length; i++) + if (monoByPower[i] is null) + monoByPower[i] = 0; + return true; + } + } +} diff --git a/Tests/UnitTests/Core/PolyParser.cs b/Tests/UnitTests/Core/PolyParser.cs new file mode 100644 index 000000000..cabfa0ab4 --- /dev/null +++ b/Tests/UnitTests/Core/PolyParser.cs @@ -0,0 +1,97 @@ +using AngouriMath; +using static AngouriMath.Entity.Number; +using Xunit; +using AngouriMath.Functions; + +namespace UnitTests.Core +{ + public class PolyParser + { + // Workaround to avoid ToString which results in SO in RC1 + private void AssertEntityEqual(Entity a, Entity b) + { + b = b.InnerSimplify(); + if (a != b) + Assert.False(true, $"{a.Stringize()} is not {b.Stringize()}"); + } + + private void Fail() + { + Assert.True(false, "Not parsed for some reason"); + } + + [Fact] + public void TestLinear1() + { + if (TreeAnalyzer.TryGetPolyLinear("a x + b", "x", out var a, out var b)) + { + AssertEntityEqual("a", a); + AssertEntityEqual("b", b); + } + else + Fail(); + } + + [Fact] + public void TestLinear2() + { + if (TreeAnalyzer.TryGetPolyLinear("a x - b", "x", out var a, out var b)) + { + AssertEntityEqual("a", a); + AssertEntityEqual("-b", b); + } + else + Fail(); + } + + [Fact] + public void TestLinear3() + { + if (TreeAnalyzer.TryGetPolyLinear("a x - b + 3x + x", "x", out var a, out var b)) + { + AssertEntityEqual("a + 3 + 1", a); + AssertEntityEqual("-b", b); + } + else + Fail(); + } + + [Fact] + public void TestLinear4() + { + Assert.False(TreeAnalyzer.TryGetPolyLinear("a x - b + 3x + x2", "x", out _, out _)); + } + + [Fact] + public void TestLinear5() + { + Assert.False(TreeAnalyzer.TryGetPolyLinear("a x - b + 3x + x^(-999)", "x", out _, out _)); + } + + [Fact] + public void TestQuadratic1() + { + if (TreeAnalyzer.TryGetPolyQuadratic("a x2 + b x + c", "x", out var a, out var b, out var c)) + { + AssertEntityEqual("a", a); + AssertEntityEqual("b", b); + AssertEntityEqual("c", c); + } + else + Fail(); + } + + [Fact] + public void TestQuadratic2() + { + if (TreeAnalyzer.TryGetPolyQuadratic("a x2 + b x2 + c", "x", out var a, out var b, out var c)) + { + AssertEntityEqual("a + b", a); + AssertEntityEqual("0", b); + AssertEntityEqual("c", c); + } + else + Fail(); + } + } +} From bf3d756550aa09abf84a27dc33b582261f912ad7 Mon Sep 17 00:00:00 2001 From: "Sasha.potapov2002" Date: Tue, 6 Oct 2020 23:13:08 +0300 Subject: [PATCH 06/12] more intergal solvers + refactoring thx to TryGetPolyLinear --- .../Integration/IndefiniteIntegralSolver.cs | 29 ++++++++--- .../Algebra/Integration/IntegralPatterns.cs | 49 ++++++------------- .../Algebra/Integration/Integration.cs | 9 ++-- Samples/Samples/Program.cs | 13 ++--- 4 files changed, 45 insertions(+), 55 deletions(-) diff --git a/AngouriMath/Functions/Continuous/Algebra/Integration/IndefiniteIntegralSolver.cs b/AngouriMath/Functions/Continuous/Algebra/Integration/IndefiniteIntegralSolver.cs index 59001af25..4028cfa7c 100644 --- a/AngouriMath/Functions/Continuous/Algebra/Integration/IndefiniteIntegralSolver.cs +++ b/AngouriMath/Functions/Continuous/Algebra/Integration/IndefiniteIntegralSolver.cs @@ -1,9 +1,4 @@ -using AngouriMath.Functions.Algebra.AnalyticalSolving; -using PeterO.Numbers; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Linq; namespace AngouriMath.Functions.Algebra { @@ -76,5 +71,27 @@ over is Entity.Powf(var @base, var power) ? } else return null; } + + internal static Entity? SolveLogarithmic(Entity expr, Entity.Variable x) => expr switch + { + Entity.Logf(var @base, var arg) => + @base.Contains(x) ? + Integration.ComputeIndefiniteIntegral(MathS.Ln(arg) / MathS.Ln(@base), x) : + arg is Entity.Powf(var y, var pow) ? // log(b, y^p) = ln(y^p) / ln(b) = ln(p) / ln(b) * ln(y) + Integration.ComputeIndefiniteIntegral(pow / MathS.Ln(@base) * MathS.Ln(y), x) : + null, + + _ => null + }; + + internal static Entity? SolveExponential(Entity expr, Entity.Variable x) => expr switch + { + Entity.Powf(var @base, var pow) => + @base.Contains(x) ? + Integration.ComputeIndefiniteIntegral(MathS.Pow(MathS.e, MathS.Ln(@base) * pow), x) : + null, + + _ => null + }; } } diff --git a/AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs b/AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs index 85efe42e3..7c59132f8 100644 --- a/AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs +++ b/AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs @@ -1,56 +1,37 @@ -using AngouriMath.Functions.Algebra; -using Antlr4.Runtime.Misc; -using System; -using System.Collections.Generic; -using System.Net; -using System.Text; - -namespace AngouriMath.Functions.Algebra +namespace AngouriMath.Functions.Algebra { internal static class IntegralPatterns { internal static Entity? TryStandardIntegrals(Entity expr, Entity.Variable x) => expr switch { Entity.Sinf(var arg) => - arg == x ? - -MathS.Cos(arg) : - arg is Entity.Mulf(var a1, var y1) && !a1.Contains(x) && y1 == x ? - -MathS.Cos(arg) / a1 : - arg is Entity.Mulf(var y2, var a2) && !a2.Contains(x) && y2 == x ? - -MathS.Cos(arg) / a2 : - arg is Entity.Divf(var y3, var a3) && !a3.Contains(x) && y3 == x ? - -MathS.Cos(arg) * a3 : - null, + TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out _) ? + -MathS.Cos(arg) / a : + null, Entity.Cosf(var arg) => - arg == x ? - MathS.Sin(arg) : - arg is Entity.Mulf(var a1, var y1) && !a1.Contains(x) && y1 == x ? - MathS.Sin(arg) / a1 : - arg is Entity.Mulf(var y2, var a2) && !a2.Contains(x) && y2 == x ? - MathS.Sin(arg) / a2 : - arg is Entity.Divf(var y3, var a3) && !a3.Contains(x) && y3 == x ? - MathS.Sin(arg) * a3 : - null, + TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out _) ? + MathS.Sin(arg) / a : + null, Entity.Tanf(var arg) => - arg == x || arg == -x ? - -MathS.Ln(MathS.Cos(arg)) : + TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out _) ? + -MathS.Ln(MathS.Cos(arg)) / a : null, Entity.Cotanf(var arg) => - arg == x || arg == -x ? - MathS.Ln(MathS.Sin(arg)) : + TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out _) ? + MathS.Ln(MathS.Sin(arg)) / a : null, Entity.Logf(var @base, var arg) => - !@base.Contains(x) && (arg == x || arg == -x) ? - (arg * MathS.Ln(arg) - arg) / MathS.Ln(@base) : + !@base.Contains(x) && TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out var b) ? + ((b / a + x) * MathS.Ln(arg) - x) / MathS.Ln(@base) : null, Entity.Powf(var @base, var power) => - !@base.Contains(x) ? - MathS.Pow(@base, power) / MathS.Ln(@base) : + !@base.Contains(x) && TreeAnalyzer.TryGetPolyLinear(power, x, out var a, out _) ? + MathS.Pow(@base, power) / (a * MathS.Ln(@base)) : null, _ => null diff --git a/AngouriMath/Functions/Continuous/Algebra/Integration/Integration.cs b/AngouriMath/Functions/Continuous/Algebra/Integration/Integration.cs index d2b0ec9e1..0907f6188 100644 --- a/AngouriMath/Functions/Continuous/Algebra/Integration/Integration.cs +++ b/AngouriMath/Functions/Continuous/Algebra/Integration/Integration.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace AngouriMath.Functions.Algebra +namespace AngouriMath.Functions.Algebra { public static partial class Integration { @@ -24,6 +20,9 @@ public static Entity ComputeIndefiniteIntegral(Entity expr, Entity.Variable x) answer = IndefiniteIntegralSolver.SolveIntegratingByParts(expr, x); if (answer is { }) return answer; + answer = IndefiniteIntegralSolver.SolveLogarithmic(expr, x); + if (answer is { }) return answer; + return new Entity.Integralf(expr, x, 1); // return as integral if nothing can be done with expression } } diff --git a/Samples/Samples/Program.cs b/Samples/Samples/Program.cs index 265030132..4d571a00e 100644 --- a/Samples/Samples/Program.cs +++ b/Samples/Samples/Program.cs @@ -1,11 +1,4 @@ using System; -using System.Collections.Immutable; -using System.Linq; -using AngouriMath; -using static AngouriMath.Entity; -using static AngouriMath.Entity.Boolean; -using static AngouriMath.MathS; -using AngouriMath.Extensions; using AngouriMath.Functions.Algebra; namespace Samples @@ -14,9 +7,9 @@ class Program { static void Main(string[] _) { - var integ = "ln(x/2)"; - var ans = Integration.ComputeIndefiniteIntegral(integ, "x"); - Console.WriteLine(ans.Simplify()); + var t = "ln((a*x+b)^c)"; + var res = Integration.ComputeIndefiniteIntegral(t, "x"); + Console.WriteLine(res.Simplify()); } } } \ No newline at end of file From 141c7e6efcac2ebc3e2e88c625fc8a42b802448e Mon Sep 17 00:00:00 2001 From: "Sasha.potapov2002" Date: Wed, 7 Oct 2020 11:49:00 +0300 Subject: [PATCH 07/12] fixed trigonometric patterns --- .../AnalyticalEquationSolver.cs | 7 +++- .../EquationSolver/ExponentialSolver.cs | 18 +++++++++ AngouriMath/Functions/Evaluation/Patterns.cs | 40 ++++--------------- Samples/Samples/Program.cs | 7 ++-- 4 files changed, 35 insertions(+), 37 deletions(-) create mode 100644 AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs diff --git a/AngouriMath/Functions/Continuous/Solvers/EquationSolver/AnalyticalEquationSolver.cs b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/AnalyticalEquationSolver.cs index 2ffb68668..168615e67 100644 --- a/AngouriMath/Functions/Continuous/Solvers/EquationSolver/AnalyticalEquationSolver.cs +++ b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/AnalyticalEquationSolver.cs @@ -153,11 +153,16 @@ static Entity TryDowncast(Entity equation, Variable x, Entity root) // // // } - // if no replacement worked, try trigonometry solver + // if no replacement worked, try exponential solver if (TrigonometricSolver.SolveLinear(expr, x) is { } trig && trig.IsFiniteSet(out var elsTrig)) return elsTrig.Select(ent => TryDowncast(expr, x, ent)).ToSet(); // // // + // if no exponential rules helped, try trigonometric solver + if (ExponentialSolver.SolveLinear(expr, x) is { } exp && exp.IsFiniteSet(out var elsExp)) + return elsExp.Select(ent => TryDowncast(expr, x, ent)).ToSet(); + // // // + // if no trigonometric rules helped, common denominator might help if (CommonDenominatorSolver.Solve(expr, x) is { } commonDenom && commonDenom.IsFiniteSet(out var elsCd)) return elsCd.Select(ent => TryDowncast(expr, x, ent)).ToSet(); diff --git a/AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs new file mode 100644 index 000000000..484821137 --- /dev/null +++ b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml; + +namespace AngouriMath.Functions.Algebra.AnalyticalSolving +{ + class ExponentialSolver + { + internal static Entity.SetNode? SolveLinear(Entity expr, Entity.Variable x) + { + var linearized = TreeAnalyzer.GatherLinearChildrenOverSumAndExpand(expr, e => e.Contains(x)); + // TODO: exponential solver in future releases + return null; + } + } +} diff --git a/AngouriMath/Functions/Evaluation/Patterns.cs b/AngouriMath/Functions/Evaluation/Patterns.cs index 6706477b1..cdbbc1ca8 100644 --- a/AngouriMath/Functions/Evaluation/Patterns.cs +++ b/AngouriMath/Functions/Evaluation/Patterns.cs @@ -182,12 +182,12 @@ Entity FactorizeFactorialMultiplications(Entity x, Entity x2, Number factConst, internal static Func TrigonometricToExponentialRules(Variable from, Variable to) => tree => { // sin(ax + b) = (t^a * e^(i*b) - t^(-a) * e^(-i*b)) / (2i) - Entity SinResult(Variable x, Number a, Entity b) => + Entity SinResult(Variable x, Entity a, Entity b) => x == from ? MathS.Pow(to, a) * (MathS.Pow(MathS.e, b * MathS.i) / (2 * MathS.i)) - MathS.Pow(to, -a) * MathS.Pow(MathS.e, -b * MathS.i) / (2 * MathS.i) : tree; // cos(ax + b) = (t^a * e^(i*b) + t^(-a) * e^(-i*b)) / 2 - Entity CosResult(Variable x, Number a, Entity b) => + Entity CosResult(Variable x, Entity a, Entity b) => x == from ? MathS.Pow(to, a) * (MathS.Pow(MathS.e, b * MathS.i) / 2) + MathS.Pow(to, -a) * MathS.Pow(MathS.e, -b * MathS.i) / 2 : tree; @@ -196,36 +196,12 @@ Entity CosResult(Variable x, Number a, Entity b) => // e.g. tan(ax + b) = -i + (2i)/(1 + e^(2i*b) t^(2a)) return tree switch { - Sinf(Variable x) => SinResult(x, 1, 0), - Sinf(Mulf(Variable x, Number a)) => SinResult(x, a, 0), - Sinf(Mulf(Number a, Variable x)) => SinResult(x, a, 0), - Sinf(Sumf(Variable x, var b)) => SinResult(x, 1, b), - Sinf(Sumf(var b, Variable x)) => SinResult(x, 1, b), - Sinf(Sumf(Mulf(Variable x, Number a), var b)) => SinResult(x, a, b), - Sinf(Sumf(Mulf(Number a, Variable x), var b)) => SinResult(x, a, b), - Sinf(Sumf(var b, Mulf(Variable x, Number a))) => SinResult(x, a, b), - Sinf(Sumf(var b, Mulf(Number a, Variable x))) => SinResult(x, a, b), - Sinf(Minusf(Variable x, var b)) => SinResult(x, 1, -b), - Sinf(Minusf(var b, Variable x)) => SinResult(x, -1, b), - Sinf(Minusf(Mulf(Variable x, Number a), var b)) => SinResult(x, a, -b), - Sinf(Minusf(Mulf(Number a, Variable x), var b)) => SinResult(x, a, -b), - Sinf(Minusf(var b, Mulf(Variable x, Number a))) => SinResult(x, -a, b), - Sinf(Minusf(var b, Mulf(Number a, Variable x))) => SinResult(x, -a, b), - Cosf(Variable x) => CosResult(x, 1, 0), - Cosf(Mulf(Variable x, Number a)) => CosResult(x, a, 0), - Cosf(Mulf(Number a, Variable x)) => CosResult(x, a, 0), - Cosf(Sumf(Variable x, var b)) => CosResult(x, 1, b), - Cosf(Sumf(var b, Variable x)) => CosResult(x, 1, b), - Cosf(Sumf(Mulf(Variable x, Number a), var b)) => CosResult(x, a, b), - Cosf(Sumf(Mulf(Number a, Variable x), var b)) => CosResult(x, a, b), - Cosf(Sumf(var b, Mulf(Variable x, Number a))) => CosResult(x, a, b), - Cosf(Sumf(var b, Mulf(Number a, Variable x))) => CosResult(x, a, b), - Cosf(Minusf(Variable x, var b)) => CosResult(x, 1, -b), - Cosf(Minusf(var b, Variable x)) => CosResult(x, -1, b), - Cosf(Minusf(Mulf(Variable x, Number a), var b)) => CosResult(x, a, -b), - Cosf(Minusf(Mulf(Number a, Variable x), var b)) => CosResult(x, a, -b), - Cosf(Minusf(var b, Mulf(Variable x, Number a))) => CosResult(x, -a, b), - Cosf(Minusf(var b, Mulf(Number a, Variable x))) => CosResult(x, -a, b), + Sinf(var arg) => TreeAnalyzer.TryGetPolyLinear(arg, from, out var a, out var b) ? + SinResult(from, a.InnerSimplify(), b.InnerSimplify()) : tree, + + Cosf(var arg) => TreeAnalyzer.TryGetPolyLinear(arg, from, out var a, out var b) ? + CosResult(from, a.InnerSimplify(), b.InnerSimplify()) : tree, + _ => tree }; }; diff --git a/Samples/Samples/Program.cs b/Samples/Samples/Program.cs index 4d571a00e..caf2a828c 100644 --- a/Samples/Samples/Program.cs +++ b/Samples/Samples/Program.cs @@ -1,5 +1,5 @@ using System; -using AngouriMath.Functions.Algebra; +using AngouriMath; namespace Samples { @@ -7,9 +7,8 @@ class Program { static void Main(string[] _) { - var t = "ln((a*x+b)^c)"; - var res = Integration.ComputeIndefiniteIntegral(t, "x"); - Console.WriteLine(res.Simplify()); + Entity t = "e^(a*x) / 0.5 / a + e^(2*a*x) + 1"; + Console.WriteLine(t.SolveEquation("x")); } } } \ No newline at end of file From 638ab4105a11457780ddd87c00651843052404e3 Mon Sep 17 00:00:00 2001 From: "Sasha.potapov2002" Date: Wed, 7 Oct 2020 12:40:18 +0300 Subject: [PATCH 08/12] added basic exponential solver (still needs improvements) --- .../EquationSolver/ExponentialSolver.cs | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs index 484821137..bd1b5d663 100644 --- a/AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs +++ b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs @@ -1,4 +1,5 @@ -using System; +using AngouriMath.Extensions; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -10,9 +11,36 @@ class ExponentialSolver { internal static Entity.SetNode? SolveLinear(Entity expr, Entity.Variable x) { - var linearized = TreeAnalyzer.GatherLinearChildrenOverSumAndExpand(expr, e => e.Contains(x)); - // TODO: exponential solver in future releases - return null; + var replacement = Entity.Variable.CreateTemp(expr.Vars); + + Func preparator = e => e switch + { + Entity.Powf(var @base, var arg) => + TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out var b) ? + MathS.Pow(MathS.e, b) * MathS.Pow(MathS.Pow(MathS.e, x), MathS.Ln(@base) * a) : e, + + _ => e + }; + + Func replacer = e => e switch + { + Entity.Powf(var @base, var arg) => + @base == MathS.e && arg == x ? + replacement : e, + + _ => e, + }; + + expr = expr.Replace(preparator); + expr = expr.Replace(replacer); + + if (expr.Contains(x)) return null; // cannot be solved, not a pure exponential + + expr = expr.InnerSimplify(); + if (AnalyticalEquationSolver.Solve(expr, replacement).IsFiniteSet(out var els)) + return els.Select(sol => MathS.Pow(MathS.e, x).Invert(sol, x).ToSetNode()).Unite(); + else + return null; } } } From 8aaa9b9d9afa380767695996ab64574f463b21b0 Mon Sep 17 00:00:00 2001 From: "Sasha.potapov2002" Date: Thu, 8 Oct 2020 12:05:51 +0300 Subject: [PATCH 09/12] imporved exponential solver, now is solved equation when bases are equal --- .../AnalyticalEquationSolver.cs | 13 ++-- .../EquationSolver/ExponentialSolver.cs | 61 ++++++++++++++++--- Samples/Samples/Program.cs | 2 +- 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/AngouriMath/Functions/Continuous/Solvers/EquationSolver/AnalyticalEquationSolver.cs b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/AnalyticalEquationSolver.cs index 168615e67..935cec839 100644 --- a/AngouriMath/Functions/Continuous/Solvers/EquationSolver/AnalyticalEquationSolver.cs +++ b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/AnalyticalEquationSolver.cs @@ -153,14 +153,19 @@ static Entity TryDowncast(Entity equation, Variable x, Entity root) // // // } - // if no replacement worked, try exponential solver + // if no replacement worked, try exponential-multiplicative solver if (TrigonometricSolver.SolveLinear(expr, x) is { } trig && trig.IsFiniteSet(out var elsTrig)) return elsTrig.Select(ent => TryDowncast(expr, x, ent)).ToSet(); // // // - // if no exponential rules helped, try trigonometric solver - if (ExponentialSolver.SolveLinear(expr, x) is { } exp && exp.IsFiniteSet(out var elsExp)) - return elsExp.Select(ent => TryDowncast(expr, x, ent)).ToSet(); + // if no exponential-multiplicative rules helped, try exponential-linear solver + if (ExponentialSolver.SolveMultiplicative(expr, x) is { } expMul && expMul.IsFiniteSet(out var elsExpMul)) + return elsExpMul.Select(ent => TryDowncast(expr, x, ent)).ToSet(); + // // // + + // if no exponential-linear rules helped, try trigonometric solver + if (ExponentialSolver.SolveLinear(expr, x) is { } expLin && expLin.IsFiniteSet(out var elsExpLin)) + return elsExpLin.Select(ent => TryDowncast(expr, x, ent)).ToSet(); // // // // if no trigonometric rules helped, common denominator might help diff --git a/AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs index bd1b5d663..cf9eca66e 100644 --- a/AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs +++ b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs @@ -15,18 +15,18 @@ class ExponentialSolver Func preparator = e => e switch { - Entity.Powf(var @base, var arg) => - TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out var b) ? - MathS.Pow(MathS.e, b) * MathS.Pow(MathS.Pow(MathS.e, x), MathS.Ln(@base) * a) : e, + Entity.Powf(var @base, var arg) when + TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out var b) => + MathS.Pow(MathS.e, b) * MathS.Pow(MathS.Pow(MathS.e, x), MathS.Ln(@base) * a), - _ => e + _ => e, }; Func replacer = e => e switch { - Entity.Powf(var @base, var arg) => - @base == MathS.e && arg == x ? - replacement : e, + Entity.Powf(var @base, var arg) + when @base == MathS.e && arg == x + => replacement, _ => e, }; @@ -37,10 +37,55 @@ class ExponentialSolver if (expr.Contains(x)) return null; // cannot be solved, not a pure exponential expr = expr.InnerSimplify(); - if (AnalyticalEquationSolver.Solve(expr, replacement).IsFiniteSet(out var els)) + if (AnalyticalEquationSolver.Solve(expr, replacement).IsFiniteSet(out var els) && els.Any()) return els.Select(sol => MathS.Pow(MathS.e, x).Invert(sol, x).ToSetNode()).Unite(); else return null; } + + internal static Entity.SetNode? SolveMultiplicative(Entity expr, Entity.Variable x) + { + Entity? substitution = null; + Entity ApplyPowerTransform(Entity @base, Entity arg) + { + var mults = Entity.Mulf.LinearChildren(arg); + if (mults.Count() == 0) return MathS.Pow(@base, arg); + + Entity innerPower = 1; + Entity outerPower = 1; + foreach(var mult in mults) + { + if (mult.InnerSimplify() is Entity.Number) + outerPower = outerPower * mult; + else + innerPower = innerPower * mult; + } + substitution = MathS.Pow(@base, innerPower); + return MathS.Pow(substitution, outerPower.InnerSimplify()); + } + + Func powerTransform = e => e switch + { + Entity.Powf(var @base, var arg) + when @base == x && !arg.Contains(x) => + ApplyPowerTransform(@base, arg), + + _ => e, + }; + + expr = expr.Replace(powerTransform); + if (substitution is null) return null; + + var replacement = Entity.Variable.CreateTemp(expr.Vars); + expr = expr.Substitute(substitution, replacement); + + if (expr.Contains(x)) return null; // cannot be solved, not a multiplicative exponenial equation + + expr = expr.InnerSimplify(); + if (AnalyticalEquationSolver.Solve(expr, replacement).IsFiniteSet(out var els) && els.Any()) + return els.Select(sol => substitution.Invert(sol, x).ToSetNode()).Unite(); + else + return null; + } } } diff --git a/Samples/Samples/Program.cs b/Samples/Samples/Program.cs index caf2a828c..14ad415ef 100644 --- a/Samples/Samples/Program.cs +++ b/Samples/Samples/Program.cs @@ -7,7 +7,7 @@ class Program { static void Main(string[] _) { - Entity t = "e^(a*x) / 0.5 / a + e^(2*a*x) + 1"; + Entity t = "2^x + 4^x - 2^(3x) - 4"; Console.WriteLine(t.SolveEquation("x")); } } From 5307aa6b5d803a5868ecdf64645182fe47de17ba Mon Sep 17 00:00:00 2001 From: Angourisoft Date: Sun, 11 Oct 2020 10:20:05 +0300 Subject: [PATCH 10/12] Tests for polynomial parser fixed --- AngouriMath/Convenience/MathS.cs | 48 ++++++++++++++++++++++++++++++ Tests/UnitTests/Core/PolyParser.cs | 16 +++++----- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/AngouriMath/Convenience/MathS.cs b/AngouriMath/Convenience/MathS.cs index 59a3d8516..41d340dff 100644 --- a/AngouriMath/Convenience/MathS.cs +++ b/AngouriMath/Convenience/MathS.cs @@ -24,6 +24,7 @@ using AngouriMath.Functions.Algebra.NumericalSolving; using AngouriMath.Functions.Boolean; using AngouriMath.Core.Exceptions; +using System.Diagnostics.CodeAnalysis; namespace AngouriMath.Core { @@ -868,6 +869,9 @@ public static class Boolean public static class Utils { + /// + /// Performs the expansion operation over the given variable + /// public static Entity SmartExpandOver(Entity expr, Variable x) { var linChildren = Sumf.LinearChildren(expr); @@ -884,6 +888,50 @@ public static Entity SmartExpandOver(Entity expr, Variable x) return TreeAnalyzer.MultiHangBinary(nodes, (a, b) => a + b); } + + /// + /// Extracts a polynomial with integer powers + /// + /// From which to extract the polynomial + /// Over which variable to extract the polynomial + /// + /// Where to put the dictionary, whose keys + /// are powers, and values - coefficients + /// + /// Whether the input expression is a valid polynomial + public static bool TryGetPolynomial(Entity expr, Variable variable, + [NotNullWhen(true)] out Dictionary? dst) + => TreeAnalyzer.TryGetPolynomial(expr, variable, out dst); + + /// + /// Extracts the linear coefficient and the bias over a variable + /// a x + b + /// + /// From which to extract the linear function + /// Over which to extract + /// The linear coefficient + /// The bias + /// Whether the extract was successful + public static bool TryGetPolyLinear(Entity expr, Variable variable, + [NotNullWhen(true)] out Entity? a, + [NotNullWhen(true)] out Entity? b) + => TreeAnalyzer.TryGetPolyLinear(expr, variable, out a, out b); + + /// + /// Extracts the quadratic coefficient, linear coefficient and the bias over a variable + /// a x ^ 2 + b x + c + /// + /// From which to extract the quadratic function + /// Over which to extract + /// The quadratic coefficient + /// The linear coefficient + /// The bias + /// Whether the extract was successful + public static bool TryGetPolyQuadratic(Entity expr, Variable variable, + [NotNullWhen(true)] out Entity? a, + [NotNullWhen(true)] out Entity? b, + [NotNullWhen(true)] out Entity? c) + => TreeAnalyzer.TryGetPolyQuadratic(expr, variable, out a, out b, out c); } } } diff --git a/Tests/UnitTests/Core/PolyParser.cs b/Tests/UnitTests/Core/PolyParser.cs index cabfa0ab4..d57e052fd 100644 --- a/Tests/UnitTests/Core/PolyParser.cs +++ b/Tests/UnitTests/Core/PolyParser.cs @@ -10,7 +10,7 @@ public class PolyParser // Workaround to avoid ToString which results in SO in RC1 private void AssertEntityEqual(Entity a, Entity b) { - b = b.InnerSimplify(); + b = b.InnerSimplified; if (a != b) Assert.False(true, $"{a.Stringize()} is not {b.Stringize()}"); } @@ -23,7 +23,7 @@ private void Fail() [Fact] public void TestLinear1() { - if (TreeAnalyzer.TryGetPolyLinear("a x + b", "x", out var a, out var b)) + if (MathS.Utils.TryGetPolyLinear("a x + b", "x", out var a, out var b)) { AssertEntityEqual("a", a); AssertEntityEqual("b", b); @@ -35,7 +35,7 @@ public void TestLinear1() [Fact] public void TestLinear2() { - if (TreeAnalyzer.TryGetPolyLinear("a x - b", "x", out var a, out var b)) + if (MathS.Utils.TryGetPolyLinear("a x - b", "x", out var a, out var b)) { AssertEntityEqual("a", a); AssertEntityEqual("-b", b); @@ -47,7 +47,7 @@ public void TestLinear2() [Fact] public void TestLinear3() { - if (TreeAnalyzer.TryGetPolyLinear("a x - b + 3x + x", "x", out var a, out var b)) + if (MathS.Utils.TryGetPolyLinear("a x - b + 3x + x", "x", out var a, out var b)) { AssertEntityEqual("a + 3 + 1", a); AssertEntityEqual("-b", b); @@ -59,19 +59,19 @@ public void TestLinear3() [Fact] public void TestLinear4() { - Assert.False(TreeAnalyzer.TryGetPolyLinear("a x - b + 3x + x2", "x", out _, out _)); + Assert.False(MathS.Utils.TryGetPolyLinear("a x - b + 3x + x2", "x", out _, out _)); } [Fact] public void TestLinear5() { - Assert.False(TreeAnalyzer.TryGetPolyLinear("a x - b + 3x + x^(-999)", "x", out _, out _)); + Assert.False(MathS.Utils.TryGetPolyLinear("a x - b + 3x + x^(-999)", "x", out _, out _)); } [Fact] public void TestQuadratic1() { - if (TreeAnalyzer.TryGetPolyQuadratic("a x2 + b x + c", "x", out var a, out var b, out var c)) + if (MathS.Utils.TryGetPolyQuadratic("a x2 + b x + c", "x", out var a, out var b, out var c)) { AssertEntityEqual("a", a); AssertEntityEqual("b", b); @@ -84,7 +84,7 @@ public void TestQuadratic1() [Fact] public void TestQuadratic2() { - if (TreeAnalyzer.TryGetPolyQuadratic("a x2 + b x2 + c", "x", out var a, out var b, out var c)) + if (MathS.Utils.TryGetPolyQuadratic("a x2 + b x2 + c", "x", out var a, out var b, out var c)) { AssertEntityEqual("a + b", a); AssertEntityEqual("0", b); From 1084013dad42d6e03c68e817ee8c691756e546c2 Mon Sep 17 00:00:00 2001 From: Angourisoft Date: Sun, 11 Oct 2020 13:02:41 +0300 Subject: [PATCH 11/12] Sequential integration --- .../Evaluation.Continuous.Classes.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/AngouriMath/Functions/Evaluation/Evaluation.Continuous.Classes.cs b/AngouriMath/Functions/Evaluation/Evaluation.Continuous.Classes.cs index b795d23a4..2e37d6b35 100644 --- a/AngouriMath/Functions/Evaluation/Evaluation.Continuous.Classes.cs +++ b/AngouriMath/Functions/Evaluation/Evaluation.Continuous.Classes.cs @@ -380,22 +380,27 @@ Var.InnerSimplified is Variable var } public partial record Integralf { + private Entity SequentialIntegrating(Entity expr, Variable var, int iterations) + { + if (iterations < 0) + return this; + var changed = expr; + for (int i = 0; i < iterations; i++) + changed = Integration.ComputeIndefiniteIntegral(changed.InnerSimplified, var); + return changed; + } + protected override Entity InnerEval() => (Expression.Evaled, Var.Evaled, Iterations) switch { (Tensor expr, var var, var iters) => expr.Elementwise(n => new Integralf(n, var, iters).Evaled), (var expr, _, 0) => expr, // TODO: consider Derivative for negative cases - (var expr, Variable var, int asInt) => - Iterations == 1 ? - Integration.ComputeIndefiniteIntegral(expr, var) - : throw FutureReleaseException.Raised("Integral powers are not implemented yet", "1.2.3"), + (var expr, Variable var, int asInt) => SequentialIntegrating(expr, var, asInt), _ => this }; internal override Entity InnerSimplify() => - Var.InnerSimplified is Variable var && Iterations == 1 ? - Integration.ComputeIndefiniteIntegral(Expression, var) - : throw FutureReleaseException.Raised("Integral powers are not implemented yet", "1.2.3"); + Var.InnerSimplified is Variable var ? SequentialIntegrating(Expression.InnerSimplified, var, Iterations) : this; } public partial record Limitf { From 34022e6de24f67c593ab8edd70a2bc8bd3197f58 Mon Sep 17 00:00:00 2001 From: Angourisoft Date: Sun, 11 Oct 2020 18:27:00 +0300 Subject: [PATCH 12/12] ifs -> switch --- .../AnalyticalInequalitySolver.cs | 5 +---- AngouriMath/Functions/Evaluation/Patterns.cs | 22 ++++++++----------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/AngouriMath/Functions/Continuous/Solvers/InequalitySolver/AnalyticalInequalitySolver.cs b/AngouriMath/Functions/Continuous/Solvers/InequalitySolver/AnalyticalInequalitySolver.cs index 601ef9d2c..f5aee2412 100644 --- a/AngouriMath/Functions/Continuous/Solvers/InequalitySolver/AnalyticalInequalitySolver.cs +++ b/AngouriMath/Functions/Continuous/Solvers/InequalitySolver/AnalyticalInequalitySolver.cs @@ -20,10 +20,7 @@ internal static class AnalyticalInequalitySolver /// internal static Set Solve(Entity expr, Variable x) { - if (expr is Minusf(var v, var c) && v == x && !c.ContainsNode(x)) - return new Interval(c, false, Number.Real.PositiveInfinity, false); - if (expr is Minusf(var c1, var v1) && v1 == x && !c1.ContainsNode(x)) - return new Interval(Number.Real.NegativeInfinity, false, c1, false); + // TODO: poly parser throw FutureReleaseException.Raised("Inequalities are not implemented yet", "1.2.1"); } } diff --git a/AngouriMath/Functions/Evaluation/Patterns.cs b/AngouriMath/Functions/Evaluation/Patterns.cs index 438b612e5..06d1446ba 100644 --- a/AngouriMath/Functions/Evaluation/Patterns.cs +++ b/AngouriMath/Functions/Evaluation/Patterns.cs @@ -577,19 +577,15 @@ private static bool IsZero(Entity entity) // Suggestions to refactor this? private static bool OppositeSigns(ComparisonSign left, ComparisonSign right) - { - if (left is Lessf) - return right is Greaterf or GreaterOrEqualf or Equalsf; - if (left is LessOrEqualf) - return right is Greaterf; - if (left is Greaterf) - return right is Lessf or LessOrEqualf or Equalsf; - if (left is GreaterOrEqualf) - return right is Lessf; - if (left is Equalsf) - return right is Lessf or Greaterf; - return false; - } + => left switch + { + Lessf => right is Greaterf or GreaterOrEqualf or Equalsf, + LessOrEqualf => right is Greaterf, + Greaterf => right is Lessf or LessOrEqualf or Equalsf, + GreaterOrEqualf => right is Lessf, + Equalsf => right is Lessf or Greaterf, + _ => false + }; internal static Entity InequalityEqualityRules(Entity x) => x switch {