internal static Entity?SolveBySubstitution(Entity expr, Variable x) { // For now we remove all provideds from an expression // Is it a correct thing to do? var res = expr.Replace(e => e is Providedf(var expr, _) ? expr : e).Substitute(x, Infinity); if (res.Evaled is Complex limit) { MultithreadingFunctional.ExitIfCancelled(); if (limit == Real.NaN) { return(null); } if (!limit.RealPart.IsFinite) { return(limit.RealPart); // TODO: sometimes we get { oo + value * i } so we assume it is just infinity } if (limit == Integer.Zero) { return(limit); } return(res); } return(null); }
// solves equation f(sin(x), cos(x), tan(x), cot(x)) for x internal static bool TrySolveLinear(Entity expr, Variable variable, out Set res) { res = Empty; var replacement = Variable.CreateTemp(expr.Vars); expr = expr.Replace(Patterns.NormalTrigonometricForm); expr = expr.Replace(Patterns.TrigonometricToExponentialRules(variable, replacement)); MultithreadingFunctional.ExitIfCancelled(); // if there is still original variable after replacements, // equation is not in a form f(sin(x), cos(x), tan(x), cot(x)) if (expr.ContainsNode(variable)) { return(false); } if (AnalyticalEquationSolver.Solve(expr, replacement) is FiniteSet els) { MultithreadingFunctional.ExitIfCancelled(); res = (Set)els.Select(sol => MathS.Pow(MathS.e, MathS.i * variable).Invert(sol, variable).ToSet()).Unite().InnerSimplified; return(true); } else { return(false); } }
internal static bool TrySolve(Entity expr, Variable x, out Set dst) { dst = Set.Empty; var children = TreeAnalyzer.GatherLinearChildrenOverSumAndExpand( expr, entity => entity.ContainsNode(x) ); if (children is null) { return(false); } Entity normalPolynom = 0; var fractioned = new List <(Entity multiplier, List <(Entity main, Integer pow)> fracs)>(); // Use PowerRules to replace sqrt(f(x))^2 => f(x) foreach (var child in children.Select(c => c.InnerSimplified.Replace(Patterns.PowerRules))) { (Entity multiplier, List <(Entity main, Integer pow)> fracs)potentialFraction; potentialFraction.multiplier = 1; potentialFraction.fracs = new List <(Entity main, Integer pow)>(); foreach (var mpChild in Mulf.LinearChildren(child)) { if (!(mpChild is Powf(var @base, Number num and not Integer))) // (x + 3) ^ 3 { potentialFraction.multiplier *= mpChild; continue; } if (num is not Rational fracNum) { return(false); // (x + 1)^0.2348728 } var newChild = MathS.Pow(@base, fracNum.ERational.Numerator).InnerSimplified; var den = fracNum.ERational.Denominator; potentialFraction.fracs.Add((newChild, den)); MultithreadingFunctional.ExitIfCancelled(); } if (potentialFraction.fracs.Count > 0) { fractioned.Add(potentialFraction); } else { normalPolynom += child; } } if (fractioned.Count == 0) { return(false); // means that one can either be solved polynomially or unsolvable at all }
internal static Entity?SolveAsPolynomial(Entity expr, Variable x) { if (ParseAsPolynomial(expr, x) is { } mono) { MultithreadingFunctional.ExitIfCancelled(); var maxPower = mono.Keys.Max(); return (maxPower.IsZero ? mono[maxPower] : maxPower.IsNegative ? 0 : mono[maxPower].Evaled is Complex power ? Infinity * power : Infinity * mono[maxPower]); }
/// <summary>Finds all alternative forms of an expression</summary> internal static IEnumerable <Entity> Alternate(Entity src, int level) { if (src is FiniteSet ss) { return new[] { ss.Apply(ent => ent.Simplify()).InnerSimplified } } ; if (src is Number or Variable or Entity.Boolean) { return new[] { src } } ; var stage1 = src.InnerSimplified; if (stage1 is Number or Variable or Entity.Boolean) { return new[] { stage1 } } ; // List of criteria for expr's complexity var history = new SortedDictionary <double, HashSet <Entity> >(); void AddHistory(Entity expr) { void __IterAddHistory(Entity expr) { var refexpr = expr.Replace(Patterns.SortRules(TreeAnalyzer.SortLevel.HIGH_LEVEL)).InnerSimplified; var compl1 = refexpr.SimplifiedRate; var compl2 = expr.SimplifiedRate; var n = compl1 > compl2 ? expr : refexpr; var ncompl = Math.Min(compl2, compl1); if (history.TryGetValue(ncompl, out var ncomplList)) { ncomplList.Add(n); } else { history[ncompl] = new HashSet <Entity> { n } }; } __IterAddHistory(expr); __IterAddHistory(expr.Replace(Patterns.InvertNegativePowers)); MultithreadingFunctional.ExitIfCancelled(); } AddHistory(stage1); var res = stage1; for (int i = 0; i < Math.Abs(level); i++) { res = res.Replace(Patterns.SortRules(i switch { 1 => TreeAnalyzer.SortLevel.MIDDLE_LEVEL, 2 => TreeAnalyzer.SortLevel.LOW_LEVEL, _ => TreeAnalyzer.SortLevel.HIGH_LEVEL })).InnerSimplified; if (res.Nodes.Any(child => child is Powf)) { AddHistory(res = res.Replace(Patterns.PowerRules).InnerSimplified); } AddHistory(res = SimplifyChildren(res)); AddHistory(res = res.Replace(Patterns.InvertNegativePowers).Replace(Patterns.DivisionPreparingRules).InnerSimplified); AddHistory(res = res.Replace(Patterns.PolynomialLongDivision).InnerSimplified); if (res.Nodes.Any(child => child is TrigonometricFunction)) { var res1 = res.Replace(Patterns.ExpandTrigonometricRules).InnerSimplified; AddHistory(res = res.Replace(Patterns.TrigonometricRules).Replace(Patterns.CommonRules).InnerSimplified); AddHistory(res1); res = PickSimplest(res, res1); AddHistory(res = res.Replace(Patterns.CollapseTrigonometricFunctions).Replace(Patterns.TrigonometricRules)); } if (res.Nodes.Any(child => child is Statement)) { AddHistory(res = res.Replace(Patterns.BooleanRules).InnerSimplified); } if (res.Nodes.Any(child => child is ComparisonSign)) { AddHistory(res = res.Replace(Patterns.InequalityEqualityRules).InnerSimplified); } if (res.Nodes.Any(child => child is Factorialf)) { AddHistory(res = res.Replace(Patterns.ExpandFactorialDivisions).InnerSimplified); AddHistory(res = res.Replace(Patterns.FactorizeFactorialMultiplications).InnerSimplified); } if (res.Nodes.Any(child => child is Powf or Logf)) { AddHistory(res = res.Replace(Patterns.PowerRules).InnerSimplified); } if (res.Nodes.Any(child => child is Set)) { AddHistory(res = res.Replace(Patterns.SetOperatorRules).InnerSimplified); } if (res.Nodes.Any(child => child is Phif)) { AddHistory(res = res.Replace(Patterns.PhiFunctionRules).InnerSimplified); } Entity?possiblePoly = null; foreach (var var in res.Vars) { if (TryPolynomial(res, var, out var resPoly) && (possiblePoly is null || resPoly.Complexity < possiblePoly.Complexity)) { AddHistory(possiblePoly = resPoly); } } if (possiblePoly is { } && possiblePoly.Complexity < res.Complexity)
/// <summary>Finds all alternative forms of an expression</summary> internal static IEnumerable <Entity> Alternate(Entity src, int level) { if (src is FiniteSet ss) { return new[] { ss.Apply(ent => ent.Simplify()).InnerSimplified } } ; if (src is Number or Variable or Entity.Boolean) { return new[] { src } } ; var stage1 = src.InnerSimplified; #if DEBUG if (MathS.Diagnostic.CatchOnSimplify.Value(stage1)) { throw new MathS.Diagnostic.DiagnosticCatchException(); } #endif if (stage1 is Number or Variable or Entity.Boolean) { return new[] { stage1 } } ; // List of criteria for expr's complexity var history = new SortedDictionary <double, HashSet <Entity> >(); void AddHistory(Entity expr) { #if DEBUG if (MathS.Diagnostic.CatchOnSimplify.Value(expr)) { throw new MathS.Diagnostic.DiagnosticCatchException(); } #endif void __IterAddHistory(Entity expr) { var refexpr = expr.Replace(Patterns.SortRules(TreeAnalyzer.SortLevel.HIGH_LEVEL)).InnerSimplified; var compl1 = refexpr.SimplifiedRate; var compl2 = expr.SimplifiedRate; var n = compl1 > compl2 ? expr : refexpr; var ncompl = Math.Min(compl2, compl1); if (history.TryGetValue(ncompl, out var ncomplList)) { ncomplList.Add(n); } else { history[ncompl] = new HashSet <Entity> { n } }; } __IterAddHistory(expr); __IterAddHistory(expr.Replace(Patterns.InvertNegativePowers)); MultithreadingFunctional.ExitIfCancelled(); } AddHistory(stage1); var res = stage1; for (int i = 0; i < Math.Abs(level); i++) { var sortLevel = i switch { 1 => TreeAnalyzer.SortLevel.MIDDLE_LEVEL, 2 => TreeAnalyzer.SortLevel.LOW_LEVEL, _ => TreeAnalyzer.SortLevel.HIGH_LEVEL }; res = res.Replace(Patterns.SortRules(sortLevel)).InnerSimplified; if (res.Nodes.Any(child => child is Powf)) { AddHistory(res = res.Replace(Patterns.PowerRules).InnerSimplified); } AddHistory(res = SimplifyChildren(res)); AddHistory(res = res.Replace(Patterns.InvertNegativePowers).Replace(Patterns.DivisionPreparingRules).InnerSimplified); AddHistory(res = res.Replace(Patterns.PolynomialLongDivision).InnerSimplified); AddHistory(res = res.Replace(Patterns.NormalTrigonometricForm).InnerSimplified); AddHistory(res = res.Replace(Patterns.CollapseMultipleFractions).InnerSimplified); AddHistory(res = res.Replace(e => Patterns.FractionCommonDenominatorRules(e, sortLevel)).InnerSimplified); AddHistory(res = res.Replace(Patterns.InvertNegativePowers).Replace(Patterns.DivisionPreparingRules).InnerSimplified); AddHistory(res = res.Replace(Patterns.PowerRules).InnerSimplified); AddHistory(res = res.Replace(Patterns.TrigonometricRules).InnerSimplified); AddHistory(res = res.Replace(Patterns.CollapseTrigonometricFunctions).InnerSimplified); if (res.Nodes.Any(child => child is TrigonometricFunction)) { var res1 = res.Replace(Patterns.ExpandTrigonometricRules).InnerSimplified; AddHistory(res = res.Replace(Patterns.TrigonometricRules).Replace(Patterns.CommonRules).InnerSimplified); AddHistory(res1); res = PickSimplest(res, res1); AddHistory(res = res.Replace(Patterns.CollapseTrigonometricFunctions).Replace(Patterns.TrigonometricRules)); } if (res.Nodes.Any(child => child is Statement)) { AddHistory(res = res.Replace(Patterns.BooleanRules).InnerSimplified); } if (res.Nodes.Any(child => child is ComparisonSign)) { AddHistory(res = res.Replace(Patterns.InequalityEqualityRules).InnerSimplified); } if (res.Nodes.Any(child => child is Factorialf)) { AddHistory(res = res.Replace(Patterns.ExpandFactorialDivisions).InnerSimplified); AddHistory(res = res.Replace(Patterns.FactorizeFactorialMultiplications).InnerSimplified); } if (res.Nodes.Any(child => child is Powf or Logf)) { AddHistory(res = res.Replace(Patterns.PowerRules).InnerSimplified); } if (res.Nodes.Any(child => child is Set)) { var replaced = res.Replace(Patterns.SetOperatorRules); AddHistory(res = replaced.InnerSimplified); } if (res.Nodes.Any(child => child is Phif)) { AddHistory(res = res.Replace(Patterns.PhiFunctionRules).InnerSimplified); } Entity?possiblePoly = null; foreach (var var in res.Vars) { if (TryPolynomial(res, var, out var resPoly) && (possiblePoly is null || resPoly.Complexity < possiblePoly.Complexity)) { AddHistory(possiblePoly = resPoly); } } if (possiblePoly is { } && possiblePoly.Complexity < res.Complexity) { res = possiblePoly; } AddHistory(res = res.Replace(Patterns.CommonRules)); AddHistory(res = res.Replace(Patterns.NumericNeatRules)); /* * This was intended to simplify expressions as polynomials over nodes, some kind of * greatest common node and simplifying over it. However, the current algorithm does * not solve this issue completely and yet too slow to be accepted. * * AddHistory(res = TreeAnalyzer.Factorize(res)); */ res = history[history.Keys.Min()].First(); } if (level > 0) // if level < 0 we don't check whether expanded version is better { AddHistory(res.Expand().Simplify(-level)); AddHistory(res.Factorize().Simplify(-level)); } return(history.Values.SelectMany(x => x)); }
/// <summary>Tries to solve as polynomial</summary> /// <param name="expr">Polynomial of an expression</param> /// <param name="subtree"> /// The expression the polynomial of (e. g. cos(x)^2 + cos(x) + 1 is a polynomial of cos(x)) /// </param> /// <returns>A finite <see cref="Set"/> if successful, <see langword="null"/> otherwise</returns> internal static IEnumerable <Entity>?SolveAsPolynomial(Entity expr, Variable subtree) { // Safely expand the expression // Here we find all terms var children = TreeAnalyzer.GatherLinearChildrenOverSumAndExpand(expr, entity => entity.ContainsNode(subtree)); if (children is null) { return(null); } // // // // Check if all are like {1} * x^n & gather information about them var monomialsByPower = GatherMonomialInformation <EInteger, TreeAnalyzer.PrimitiveInteger>(children, subtree); if (monomialsByPower == null) { return(null); // meaning that the given equation is not polynomial } var res = ReduceCommonPower(ref monomialsByPower) ? new Entity[] { 0 } // x5 + x3 + x2 - common power is 2, one root is 0, then x3 + x + 1 : Enumerable.Empty <Entity>(); var powers = monomialsByPower.Keys.ToList(); var gcdPower = powers.Aggregate((accumulate, current) => accumulate.Gcd(current)); // // // if (gcdPower.IsZero) { gcdPower = EInteger.One; } // Change all replacements, x6 + x3 + 1 => x2 + x + 1 if (!gcdPower.Equals(EInteger.One)) { for (int i = 0; i < powers.Count; i++) { powers[i] /= gcdPower; } monomialsByPower = monomialsByPower.ToDictionary(pair => pair.Key / gcdPower, pair => pair.Value); } // // // MultithreadingFunctional.ExitIfCancelled(); var gcdPowerRoots = GetAllRootsOf1(gcdPower); Entity GetMonomialByPower(EInteger power) => monomialsByPower.TryGetValue(power, out var monomial) ? monomial.InnerSimplified : 0; powers.Sort(); if (!powers.Last().CanFitInInt32()) { return(null); } return((powers.Count, powers.Last().ToInt32Unchecked()) switch { (0, _) => null, (_, 0) => Enumerable.Empty <Entity>(), (> 2, > 4) => null, // So far, we can't solve equations of powers more than 4 // By this moment we know for sure that expr's power is <= 4, that expr is not a monomial, // and that it consists of more than 2 monomials (1, _) => new Entity[] { 0 }, (2, _) => // Solve a x ^ n + b = 0 for x ^ n -> x ^ n = -b / a MathS.Pow(subtree, Integer.Create(powers[1])) .Invert((-1 * monomialsByPower[powers[0]] / monomialsByPower[powers[1]]).InnerSimplified, subtree), (_, 2) => SolveQuadratic(GetMonomialByPower(2), GetMonomialByPower(1), GetMonomialByPower(0)), (_, 3) => SolveCubic(GetMonomialByPower(3), GetMonomialByPower(2), GetMonomialByPower(1), GetMonomialByPower(0)), (_, 4) => SolveQuartic(GetMonomialByPower(4), GetMonomialByPower(3), GetMonomialByPower(2), GetMonomialByPower(1), GetMonomialByPower(0)), _ => null } is { } resConcat