internal static void Solve(Entity expr, VariableEntity x, Set dst, bool compensateSolving) { if (expr == x) { dst.Add(0); return; } // Applies an attempt to downcast roots void DestinationAddRange(Set toAdd) { toAdd.FiniteApply(ent => TryDowncast(expr, x, ent)); dst.AddRange(toAdd); } var polyexpr = expr.DeepCopy(); Set res = PolynomialSolver.SolveAsPolynomial(polyexpr, x); if (res != null) { res.FiniteApply(e => e.InnerSimplify()); DestinationAddRange(res); return; } if (expr.entType == Entity.EntType.OPERATOR) { switch (expr.Name) { case "mulf": Solve(expr.Children[0], x, dst); Solve(expr.Children[1], x, dst); return; case "divf": bool IsSetNumeric(Set a) => a.Select(piece => piece.LowerBound().Item1).All(MathS.CanBeEvaluated); var zeroNumerators = new Set(); Solve(expr.Children[0], x, zeroNumerators); if (!IsSetNumeric(zeroNumerators)) { dst.AddRange(zeroNumerators); return; } var zeroDenominators = new Set(); Solve(expr.Children[1], x, zeroDenominators); if (!IsSetNumeric(zeroDenominators)) { dst.AddRange(zeroNumerators); return; } dst.AddRange((zeroNumerators & !zeroDenominators) as Set); return; case "powf": Solve(expr.Children[0], x, dst); return; case "minusf": if (expr.Children[1].FindSubtree(x) == null && compensateSolving) { if (expr.Children[0] == x) { dst.Add(expr.Children[1]); return; } var subs = 0; Entity lastChild = null; foreach (var child in expr.Children[0].Children) { if (child.FindSubtree(x) != null) { subs += 1; lastChild = child; } } if (subs != 1) { break; } var resInverted = TreeAnalyzer.FindInvertExpression(expr.Children[0], expr.Children[1], lastChild); foreach (var result in resInverted.FiniteSet()) { Solve(lastChild - result, x, dst, compensateSolving: true); } return; } break; } } else if (expr.entType == Entity.EntType.FUNCTION) { DestinationAddRange(TreeAnalyzer.InvertFunctionEntity(expr as FunctionEntity, 0, x)); return; } // Here we generate a unique variable name var uniqVars = MathS.Utils.GetUniqueVariables(expr); uniqVars.Pieces.Sort((a, b) => ((Entity)b).Name.Length.CompareTo(((Entity)a).Name.Length)); VariableEntity newVar = ((Entity)uniqVars.Pieces[0]).Name + "quack"; // // // // Here we find all possible replacements var replacements = new List <Tuple <Entity, Entity> >(); replacements.Add(new Tuple <Entity, Entity>(TreeAnalyzer.GetMinimumSubtree(expr, x), expr)); foreach (var alt in expr.Alternate(4).FiniteSet()) { if ((alt).FindSubtree(x) == null) { return; // in this case there is either 0 or +oo solutions } replacements.Add(new Tuple <Entity, Entity>(TreeAnalyzer.GetMinimumSubtree(alt, x), alt)); } // // // // Here we find one that has at least one solution foreach (var replacement in replacements) { Set solutions = null; if (replacement.Item1 == x) { continue; } var newExpr = replacement.Item2.DeepCopy(); TreeAnalyzer.FindAndReplace(ref newExpr, replacement.Item1, newVar); solutions = newExpr.SolveEquation(newVar); if (!solutions.IsEmpty()) { var bestReplacement = replacement.Item1; // Here we are trying to solve for this replacement Set newDst = new Set(); foreach (var solution in solutions.FiniteSet()) { var str = bestReplacement.ToString(); // TODO: make a smarter comparison than just comparison of complexities of two expressions // The idea is // similarToPrevious = ((bestReplacement - solution) - expr).Simplify() == 0 // But Simplify costs us too much time var similarToPrevious = (bestReplacement - solution).Complexity() >= expr.Complexity(); if (!compensateSolving || !similarToPrevious) { Solve(bestReplacement - solution, x, newDst, compensateSolving: true); } } DestinationAddRange(newDst); if (!dst.IsEmpty()) { break; } // // // } } // // // // if no replacement worked, try trigonometry solver if (dst.IsEmpty()) { var trigexpr = expr.DeepCopy(); res = TrigonometricSolver.SolveLinear(trigexpr, x); if (res != null) { DestinationAddRange(res); return; } } // // // // if nothing has been found so far if (dst.IsEmpty() && MathS.Settings.AllowNewton) { Set allVars = new Set(); TreeAnalyzer._GetUniqueVariables(expr, allVars); if (allVars.Count == 1) { DestinationAddRange(expr.SolveNt(x)); } } }
// sin(x) -> sin(1 * x) // sin(x) + sin(a * x) // if x * a // x * a => a * x // if ! a * x // x => 1 * x // if ! a * x + b // a * x => a * x + 0 private static Entity ReplaceTrigonometry(Entity expr, VariableEntity variable, VariableEntity replacement) { // SolveLinear should also solve tan and cotan equations, but currently Polynomial solver cannot handle big powers // uncomment lines above when it will be fixed (TODO) var sin = expr.FindSubtree(MathS.Sin(variable)); var cos = expr.FindSubtree(MathS.Cos(variable)); //var tan = expr.FindSubtree(MathS.Tan (variable)); //var cot = expr.FindSubtree(MathS.Cotan(variable)); var sinReplacement = replacement / (2 * MathS.i) - MathS.Pow(replacement, -1) / (2 * MathS.i); var cosReplacement = replacement / 2 + MathS.Pow(replacement, -1) / 2; // var tanReplacement = (1 - MathS.Sqr(replacement)) * MathS.i * MathS.Pow(MathS.Sqr(replacement) + 1, -1); // var cotReplacement = (MathS.Sqr(replacement) + 1) * MathS.i * MathS.Pow(MathS.Sqr(replacement) - 1, -1); TreeAnalyzer.FindAndReplace(ref expr, sin, sinReplacement); TreeAnalyzer.FindAndReplace(ref expr, cos, cosReplacement); // TreeAnalyzer.FindAndReplace(ref expr, tan, tanReplacement); // TreeAnalyzer.FindAndReplace(ref expr, cot, cotReplacement); // workaround for missing Patterns.Variable(x), replace all expressions, false-matching pattern to variables // which will be returned back after all actual replacements var falseReplacements = new List <KeyValuePair <VariableEntity, Entity> >(); string falseReplacementName = "trig"; void ReplaceSinSubExpression(ref Entity expr, Entity toReplace, Entity a, Entity b, VariableEntity replacement) { // sin(ax + b) = (t^a * e^(i*b) - t^(-a) * e^(-i*b)) / (2i) var resultReplacement = (MathS.Pow(replacement, a) * (MathS.Pow(MathS.e, b * MathS.i) / (2 * MathS.i)) - MathS.Pow(replacement, -a) * (MathS.Pow(MathS.e, -b * MathS.i)) / (2 * MathS.i)); TreeAnalyzer.FindAndReplace(ref expr, toReplace, resultReplacement); } void ReplaceCosSubExpression(ref Entity expr, Entity toReplace, Entity a, Entity b, VariableEntity replacement) { // cos(ax + b) = (t^a * e^(i*b) + t^(-a) * e^(-i*b)) / 2 var resultReplacement = (MathS.Pow(replacement, a) * (MathS.Pow(MathS.e, b * MathS.i) / 2) + MathS.Pow(replacement, -a) * (MathS.Pow(MathS.e, -b * MathS.i)) / 2); TreeAnalyzer.FindAndReplace(ref expr, toReplace, resultReplacement); } // checks if pattern-matching succeeded. If not, replace subexpression with some variable bool CheckIfReplacementIsSuitable(ref Entity expr, Entity found, Entity variable, Entity variableToCheckFor) { if (variable != variableToCheckFor) { var repl = Utils.FindNextIndex(expr, falseReplacementName); falseReplacements.Add(new KeyValuePair <VariableEntity, Entity>(repl, found)); TreeAnalyzer.FindAndReplace(ref expr, found, repl); return(false); } return(true); } void MatchSinUntil(Pattern p, Func <Entity, (Entity, Entity, Entity)> variableGetter) { Entity found; while ((found = expr.FindPatternSubtree(p)) != null) { (Entity x, Entity a, Entity b) = variableGetter(found.Children[0]); if (CheckIfReplacementIsSuitable(ref expr, found, x, variable)) { ReplaceSinSubExpression(ref expr, found, a, b, replacement); } } } void MatchCosUntil(Pattern p, Func <Entity, (Entity, Entity, Entity)> variableGetter) { Entity found; while ((found = expr.FindPatternSubtree(p)) != null) { (Entity x, Entity a, Entity b) = variableGetter(found.Children[0]); if (CheckIfReplacementIsSuitable(ref expr, found, x, variable)) { ReplaceCosSubExpression(ref expr, found, a, b, replacement); } } } // arg => (x, a, b) // TODO: refactor this. Move to a list var variablePattern = new Pattern(1000, Entity.PatType.VARIABLE, tree => tree.entType == Entity.EntType.VARIABLE && tree.Name == variable.Name); var pattern1 = Sinf.PHang(Patterns.const1 * variablePattern + Patterns.any1); MatchSinUntil(pattern1, arg => (arg.Children[0].Children[1], arg.Children[0].Children[0], arg.Children[1])); var pattern2 = Sinf.PHang(variablePattern * Patterns.const1 + Patterns.any1); MatchSinUntil(pattern2, arg => (arg.Children[0].Children[0], arg.Children[0].Children[1], arg.Children[1])); var pattern3 = Sinf.PHang(Patterns.any1 + Patterns.const1 * variablePattern); MatchSinUntil(pattern3, arg => (arg.Children[1].Children[1], arg.Children[1].Children[0], arg.Children[0])); var pattern4 = Sinf.PHang(Patterns.any1 + variablePattern * Patterns.const1); MatchSinUntil(pattern4, arg => (arg.Children[1].Children[0], arg.Children[1].Children[1], arg.Children[0])); var pattern5 = Sinf.PHang(Patterns.const1 * variablePattern); MatchSinUntil(pattern5, arg => (arg.Children[1], arg.Children[0], 0)); var pattern6 = Sinf.PHang(variablePattern * Patterns.const1); MatchSinUntil(pattern6, arg => (arg.Children[0], arg.Children[1], 0)); var pattern7 = Sinf.PHang(Patterns.any1 + variablePattern); MatchSinUntil(pattern7, arg => (arg.Children[1], 1, arg.Children[0])); var pattern8 = Sinf.PHang(variablePattern + Patterns.any1); MatchSinUntil(pattern8, arg => (arg.Children[0], 1, arg.Children[1])); var pattern9 = Cosf.PHang(Patterns.const1 * variablePattern + Patterns.any1); MatchCosUntil(pattern9, arg => (arg.Children[0].Children[1], arg.Children[0].Children[0], arg.Children[1])); var pattern10 = Cosf.PHang(variablePattern * Patterns.const1 + Patterns.any1); MatchCosUntil(pattern10, arg => (arg.Children[0].Children[0], arg.Children[0].Children[1], arg.Children[1])); var pattern11 = Cosf.PHang(Patterns.any1 + Patterns.const1 * variablePattern); MatchCosUntil(pattern11, arg => (arg.Children[1].Children[1], arg.Children[1].Children[0], arg.Children[0])); var pattern12 = Cosf.PHang(Patterns.any1 + variablePattern * Patterns.const1); MatchCosUntil(pattern12, arg => (arg.Children[1].Children[0], arg.Children[1].Children[1], arg.Children[0])); var pattern13 = Cosf.PHang(Patterns.const1 * variablePattern); MatchCosUntil(pattern13, arg => (arg.Children[1], arg.Children[0], 0)); var pattern14 = Cosf.PHang(variablePattern * Patterns.const1); MatchCosUntil(pattern14, arg => (arg.Children[0], arg.Children[1], 0)); var pattern15 = Cosf.PHang(Patterns.any1 + variablePattern); MatchCosUntil(pattern15, arg => (arg.Children[1], 1, arg.Children[0])); var pattern16 = Cosf.PHang(variablePattern + Patterns.any1); MatchCosUntil(pattern16, arg => (arg.Children[0], 1, arg.Children[1])); // re-substitute all false replacement to return expression to normal foreach (var repl in falseReplacements) { TreeAnalyzer.FindAndReplace(ref expr, repl.Key, repl.Value); } return(expr); }