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 override void Check() { // Can't have other name TreeAnalyzer.AssertTree(Name == "tensort", "Tensors must have Name=tensort"); // Tensor can't be scalar TreeAnalyzer.AssertTree(Dimensions > 0, "Dimensions can't be equal to 0"); // All elements are not null for (int i = 0; i < Data.Length; i++) { TreeAnalyzer.AssertTree(Data[i] != null, "One tensor's element is null"); } }
internal static Entity?SolveBySplittingSum(Entity expr, Entity.Variable x) { var splitted = TreeAnalyzer.GatherLinearChildrenOverSumAndExpand(expr, e => e.ContainsNode(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); }
public void TestEnumerator3() { Entity expr = "2 + 3 + x"; var pattern = Powf.PHang(Patterns.any1, Patterns.any2); var matches = new Set(); foreach (var match in TreeAnalyzer.GetPatternEnumerator(expr, pattern)) { matches.Add(match); } Assert.IsTrue(matches.Count == 0, "match is NOT empty, but should"); }
public void AssertExpander(Entity expr, params ComplexNumber[] toSubs) { MathS.Settings.MaxExpansionTermCount.Set(3000); var expanded = TreeAnalyzer.MultiHangBinary( TreeAnalyzer.SmartExpandOver(expr, entity => entity.FindSubtree("x") != null), "sumf", Const.PRIOR_SUM); MathS.Settings.MaxExpansionTermCount.Unset(); foreach (var toSub in toSubs) { var comparisonResult = AreEqual(expr, expanded, toSub); Assert.IsTrue(comparisonResult.equal, "E: " + comparisonResult.err + " toSub: " + toSub + " expanded: " + expanded.ToString()); } }
/// <summary> /// solves ax3 + bx2 + cx + d /// </summary> /// <param name="a"> /// Coefficient of x^3 /// </param> /// <param name="b"> /// Coefficient of x^2 /// </param> /// <param name="c"> /// Coefficient of x /// </param> /// <param name="d"> /// Free coefficient /// </param> /// <returns> /// Set of roots /// </returns> internal static Set SolveCubic(Entity a, Entity b, Entity c, Entity d) { // en: https://en.wikipedia.org/wiki/Cubic_equation // ru: https://ru.wikipedia.org/wiki/%D0%A4%D0%BE%D1%80%D0%BC%D1%83%D0%BB%D0%B0_%D0%9A%D0%B0%D1%80%D0%B4%D0%B0%D0%BD%D0%BE // TODO (to remove sympy code!) Set res; if (TreeAnalyzer.IsZero(d)) { res = SolveQuadratic(a, b, c); res.Add(0); return(res); } if (TreeAnalyzer.IsZero(a)) { return(SolveQuadratic(b, c, d)); } res = new Set(); var coeff = MathS.i * MathS.Sqrt(3) / 2; var u1 = new NumberEntity(1); var u2 = SySyn.Rational(-1, 2) + coeff; var u3 = SySyn.Rational(-1, 2) - coeff; var D0 = MathS.Sqr(b) - 3 * a * c; var D1 = (2 * MathS.Pow(b, 3) - 9 * a * b * c + 27 * MathS.Sqr(a) * d).InnerSimplify(); var C = MathS.Pow((D1 + MathS.Sqrt(MathS.Sqr(D1) - 4 * MathS.Pow(D0, 3))) / 2, Number.CreateRational(1, 3)); foreach (var uk in new List <Entity> { u1, u2, u3 }) { Entity r; if (Const.EvalIfCan(C) == 0 && Const.EvalIfCan(D0) == 0) { r = -(b + uk * C) / 3 / a; } else { r = -(b + uk * C + D0 / C / uk) / 3 / a; } res.Add(r); } return(res); }
/// <summary>Solves ax^2 + bx + c</summary> /// <param name="a">Coefficient of x^2</param> /// <param name="b">Coefficient of x</param> /// <param name="c">Free coefficient</param> /// <returns>Set of roots</returns> internal static IEnumerable <Entity> SolveQuadratic(Entity a, Entity b, Entity c) { if (TreeAnalyzer.IsZero(c)) { return(SolveLinear(a, b).Append(0)); } if (TreeAnalyzer.IsZero(a)) { return(SolveLinear(b, c)); } var D = MathS.Sqr(b) - 4 * a * c; return(new[] { ((-b - MathS.Sqrt(D)) / (2 * a)).InnerSimplified, ((-b + MathS.Sqrt(D)) / (2 * a)).InnerSimplified }); }
/// <summary> /// Solves one equation /// </summary> /// <param name="equation"></param> /// <param name="x"></param> /// <returns></returns> internal static Set Solve(Entity equation, VariableEntity x) { var res = new Set(); equation = equation.DeepCopy(); MathS.Settings.PrecisionErrorZeroRange.Set(1e-12m); MathS.Settings.FloatToRationalIterCount.Set(0); AnalyticalSolver.Solve(equation, x, res); MathS.Settings.FloatToRationalIterCount.Unset(); MathS.Settings.PrecisionErrorZeroRange.Unset(); if (res.Power == Set.PowerLevel.FINITE) { res.FiniteApply(entity => entity.InnerSimplify()); Func <Entity, Entity> simplifier = entity => entity.InnerSimplify(); Func <Entity, Entity> evaluator = entity => entity.InnerEval(); Entity collapser(Entity expr) { if (MathS.Utils.GetUniqueVariables(equation).Count == 1) { return(expr.InnerEval()); } else { return(expr.InnerSimplify()); } } var finalSet = new Set(); finalSet.FastAddingMode = true; foreach (var elem in res.FiniteSet()) { if (TreeAnalyzer.IsDefinite(elem) && TreeAnalyzer.IsDefinite(collapser(equation.Substitute(x, elem))) ) { finalSet.Add(elem); } } finalSet.FastAddingMode = false; res = finalSet; } return(res); }
public void TestEnumerator2() { Entity expr = "a^x + ((b^y + c^z)^2)^x"; var pattern = Powf.PHang(Patterns.any1, Patterns.any2); var matches = new Set(); foreach (var match in TreeAnalyzer.GetPatternEnumerator(expr, pattern)) { matches.Add(match); } Assert.IsTrue(matches.Count == 5, "not all matched were found"); foreach (var match in matches.FiniteSet()) { Assert.IsNotNull(expr.FindSubtree(match), "match is not in expression"); } }
/// <summary>Solves ax^4 + bx^3 + cx^2 + dx + e</summary> /// <param name="a">Coefficient of x^4</param> /// <param name="b">Coefficient of x^3</param> /// <param name="c">Coefficient of x^2</param> /// <param name="d">Coefficient of x</param> /// <param name="e">Free coefficient</param> /// <returns>Set of roots</returns> internal static IEnumerable <Entity> SolveQuartic(Entity a, Entity b, Entity c, Entity d, Entity e) { // en: https://en.wikipedia.org/wiki/Quartic_function // ru: https://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A4%D0%B5%D1%80%D1%80%D0%B0%D1%80%D0%B8 if (TreeAnalyzer.IsZero(e)) { return(SolveCubic(a, b, c, d).Append(0)); } if (TreeAnalyzer.IsZero(a)) { return(SolveCubic(b, c, d, e)); } var alpha = (-3 * MathS.Sqr(b) / (8 * MathS.Sqr(a)) + c / a) .InnerSimplified; var beta = (MathS.Pow(b, 3) / (8 * MathS.Pow(a, 3)) - (b * c) / (2 * MathS.Sqr(a)) + d / a) .InnerSimplified; var gamma = (-3 * MathS.Pow(b, 4) / (256 * MathS.Pow(a, 4)) + MathS.Sqr(b) * c / (16 * MathS.Pow(a, 3)) - (b * d) / (4 * MathS.Sqr(a)) + e / a) .InnerSimplified; if (beta.Evaled == 0) { return(sqrtsOf1.SelectMany(_ => sqrtsOf1, (s, t) => - b / 4 * a + s * MathS.Sqrt((-alpha + t * MathS.Sqrt(MathS.Sqr(alpha) - 4 * gamma)) / 2))); } var oneThird = Rational.Create(1, 3); var P = (-MathS.Sqr(alpha) / 12 - gamma) .InnerSimplified; var Q = (-MathS.Pow(alpha, 3) / 108 + alpha * gamma / 3 - MathS.Sqr(beta) / 8) .InnerSimplified; var R = -Q / 2 + MathS.Sqrt(MathS.Sqr(Q) / 4 + MathS.Pow(P, 3) / 27); var U = MathS.Pow(R, oneThird) .InnerSimplified; var y = (Rational.Create(-5, 6) * alpha + U + (U.Evaled == 0 ? -MathS.Pow(Q, oneThird) : -P / (3 * U))) .InnerSimplified; var W = MathS.Sqrt(alpha + 2 * y) .InnerSimplified; // Now we need to permutate all four combinations return(sqrtsOf1.SelectMany(_ => sqrtsOf1, (s, t) => - b / (4 * a) + (s * W + t * MathS.Sqrt(-(3 * alpha + 2 * y + s * 2 * beta / W))) / 2)); }
/// <summary> /// Performs scalar product operation on two vectors /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <returns></returns> internal static Entity ScalarProduct(Tensor a, Tensor b) { // TODO: Extend to tensors if (!a.IsVector() || !b.IsVector()) { throw new MathSException("Scalar product of non-vectors is not supported yet"); } // TODO: allow different shapes if (a.Shape[0] != b.Shape[0]) { throw new MathSException("Vectors should be the same size"); } var operands = new List <Entity>(); for (int i = 0; i < a.Shape[0]; i++) { operands.Add(a[i] * b[i]); } return(TreeAnalyzer.MultiHangBinary(operands, "sumf", Const.PRIOR_SUM)); }
/// <summary> /// solves ax2 + bx + c /// </summary> /// <param name="a"> /// Coefficient of x^2 /// </param> /// <param name="b"> /// Coefficient of x /// </param> /// <param name="c"> /// Free coefficient /// </param> /// <returns> /// Set of roots /// </returns> internal static Set SolveQuadratic(Entity a, Entity b, Entity c) { Set res; if (TreeAnalyzer.IsZero(c)) { res = SolveLinear(a, b); res.Add(0); return(res); } if (TreeAnalyzer.IsZero(a)) { return(SolveLinear(b, c)); } res = new Set(); var D = MathS.Sqr(b) - 4 * a * c; res.Add(((-b - MathS.Sqrt(D)) / (2 * a)).InnerSimplify()); res.Add(((-b + MathS.Sqrt(D)) / (2 * a)).InnerSimplify()); return(res); }
/// <summary> /// Finds the first occurance of a subtree that fits a pattern /// </summary> /// <param name="pattern"></param> /// <returns> /// Entity: first found subtree /// </returns> internal Entity FindPatternSubtree(Pattern pattern) { return(TreeAnalyzer.GetPatternEnumerator(this, pattern).FirstOrDefault()); }
private Entity Expand_(int level) => level <= 1 ? TreeAnalyzer.Replace(Patterns.ExpandRules, this) : TreeAnalyzer.Replace(Patterns.ExpandRules, this).Expand_(level - 1);
/// <summary> /// Finds all alternative forms of an expression /// </summary> /// <param name="src"></param> /// <param name="level"></param> /// <returns></returns> internal static Set Alternate(Entity src, int level) { if (src.entType == Entity.EntType.NUMBER || src.entType == Entity.EntType.VARIABLE) { return(new Set(src.Copy())); } var stage1 = src.InnerSimplify(); if (stage1.entType == Entity.EntType.NUMBER) { return(new Set(stage1)); } var history = new SortedDictionary <int, Entity>(); void TryInnerSimplify(ref Entity expr) { TreeAnalyzer.Sort(ref expr, TreeAnalyzer.SortLevel.HIGH_LEVEL); expr = expr.InnerSimplify(); } void __IterAddHistory(Entity expr) { Entity refexpr = expr.DeepCopy(); TryInnerSimplify(ref refexpr); var n = refexpr.Complexity() > expr.Complexity() ? expr : refexpr; history[n.Complexity()] = n; } void AddHistory(Entity expr) { __IterAddHistory(expr); Entity _res = expr; TreeAnalyzer.InvertNegativePowers(ref _res); __IterAddHistory(_res); } AddHistory(stage1); var res = stage1.DeepCopy(); for (int i = 0; i < Math.Abs(level); i++) { if (i == 0 || i > 2) { TreeAnalyzer.Sort(ref res, TreeAnalyzer.SortLevel.HIGH_LEVEL); } else if (i == 1) { TreeAnalyzer.Sort(ref res, TreeAnalyzer.SortLevel.MIDDLE_LEVEL); } else if (i == 2) { TreeAnalyzer.Sort(ref res, TreeAnalyzer.SortLevel.LOW_LEVEL); } res = res.InnerSimplify(); if (TreeAnalyzer.Optimization.ContainsPower(res)) { TreeAnalyzer.ReplaceInPlace(Patterns.PowerRules, ref res); AddHistory(res); } { TreeAnalyzer.InvertNegativePowers(ref res); TreeAnalyzer.InvertNegativeMultipliers(ref res); TreeAnalyzer.Sort(ref res, TreeAnalyzer.SortLevel.HIGH_LEVEL); AddHistory(res); TreeAnalyzer.ReplaceInPlace(Patterns.CommonRules, ref res); AddHistory(res); TreeAnalyzer.InvertNegativePowers(ref res); } { TreeAnalyzer.InvertNegativePowers(ref res); TreeAnalyzer.ReplaceInPlace(Patterns.DivisionPreparingRules, ref res); res = res.InnerSimplify(); TreeAnalyzer.FindDivisors(ref res, (num, denom) => !MathS.CanBeEvaluated(num) && !MathS.CanBeEvaluated(denom)); } res = res.InnerSimplify(); if (TreeAnalyzer.Optimization.ContainsTrigonometric(res)) { var res1 = res.DeepCopy(); TreeAnalyzer.ReplaceInPlace(Patterns.TrigonometricRules, ref res); AddHistory(res); TreeAnalyzer.ReplaceInPlace(Patterns.ExpandTrigonometricRules, ref res1); AddHistory(res1); res = res.Complexity() > res1.Complexity() ? res1 : res; } if (TreeAnalyzer.Optimization.ContainsPower(res)) { TreeAnalyzer.ReplaceInPlace(Patterns.PowerRules, ref res); AddHistory(res); } AddHistory(res); res = history[history.Keys.Min()].DeepCopy(); } if (level > 0) // if level < 0 we don't check whether expanded version is better { var expanded = res.Expand().Simplify(-level); AddHistory(expanded); var collapsed = res.Collapse().Simplify(-level); AddHistory(collapsed); } return(new Set(history.Values.Select(p => (Piece)p).ToArray())); }
internal Node Sort(SortLevel level) { if (Left != null) { Left = Left.Sort(level); } if (Right != null) { Right = Right.Sort(level); } if (!(this is BinaryOperatorNode)) { return(Clone() as Node); } BinaryOperatorNode binOp = this as BinaryOperatorNode; if (binOp.Operator != BinaryOperator.Add && binOp.Operator != BinaryOperator.Subtract && binOp.Operator != BinaryOperator.Multiply && binOp.Operator != BinaryOperator.Divide) { return(Clone() as Node); } bool isSum = binOp.Operator == BinaryOperator.Add || binOp.Operator == BinaryOperator.Subtract; List <Tuple <Node, TreeAnalyzer.LinearChildTag> > children = TreeAnalyzer.LinearChildren(this, isSum ? BinaryOperator.Add : BinaryOperator.Multiply, isSum ? BinaryOperator.Subtract : BinaryOperator.Divide); List <List <Tuple <Node, TreeAnalyzer.LinearChildTag> > > groups = TreeAnalyzer.groupByHash(children, level); List <Tuple <Node, TreeAnalyzer.LinearChildTag> > groupedChildren = new List <Tuple <Node, TreeAnalyzer.LinearChildTag> >(); foreach (List <Tuple <Node, TreeAnalyzer.LinearChildTag> > group in groups) { groupedChildren.Add(TreeAnalyzer.internalMultihang(group, isSum ? BinaryOperator.Add : BinaryOperator.Multiply, isSum ? BinaryOperator.Subtract : BinaryOperator.Divide)); } return(TreeAnalyzer.MultiHang(groupedChildren, isSum ? BinaryOperator.Add : BinaryOperator.Multiply, isSum ? BinaryOperator.Subtract : BinaryOperator.Divide)); }
/// <summary> /// Collapses an equation trying to eliminate as many power-uses as possible ( e. g. x * 3 + x * y = x * (3 + y) ) /// </summary> /// <param name="level"> /// The number of iterations (increase this argument if some collapse operations are still available) /// </param> /// <returns></returns> public Entity Collapse(int level) => level <= 1 ? TreeAnalyzer.Replace(Patterns.CollapseRules, this) : TreeAnalyzer.Replace(Patterns.CollapseRules, this).Collapse(level - 1);
/// <summary> /// Finds all alternative forms of an expression /// </summary> /// <param name="src"></param> /// <param name="level"></param> /// <returns></returns> internal static Set Alternate(Entity src, int level) { if (src.entType == Entity.EntType.NUMBER || src.entType == Entity.EntType.VARIABLE) { return(new Set(src.Copy())); } var stage1 = src.InnerSimplify(); if (stage1.entType == Entity.EntType.NUMBER) { return(new Set(stage1)); } var history = new SortedDictionary <int, List <Entity> >(); void TryInnerSimplify(ref Entity expr) { TreeAnalyzer.Sort(ref expr, TreeAnalyzer.SortLevel.HIGH_LEVEL); expr = expr.InnerSimplify(); } // List of criterians of expr's complexity int CountExpressionComplexity(Entity expr) => MathS.Settings.ComplexityCriteria.Value(expr); void __IterAddHistory(Entity expr) { Entity refexpr = expr.DeepCopy(); TryInnerSimplify(ref refexpr); var compl1 = CountExpressionComplexity(refexpr); var compl2 = CountExpressionComplexity(expr); var n = compl1 > compl2 ? expr : refexpr; var ncompl = Math.Min(compl2, compl1); if (!history.ContainsKey(ncompl)) { history[ncompl] = new List <Entity>(); } history[ncompl].Add(n); } void AddHistory(Entity expr) { __IterAddHistory(expr); Entity _res = expr; TreeAnalyzer.InvertNegativePowers(ref _res); __IterAddHistory(_res); } AddHistory(stage1); var res = stage1.DeepCopy(); for (int i = 0; i < Math.Abs(level); i++) { if (i == 0 || i > 2) { TreeAnalyzer.Sort(ref res, TreeAnalyzer.SortLevel.HIGH_LEVEL); } else if (i == 1) { TreeAnalyzer.Sort(ref res, TreeAnalyzer.SortLevel.MIDDLE_LEVEL); } else if (i == 2) { TreeAnalyzer.Sort(ref res, TreeAnalyzer.SortLevel.LOW_LEVEL); } res = res.InnerSimplify(); if (TreeAnalyzer.Optimization.ContainsPower(res)) { TreeAnalyzer.ReplaceInPlace(Patterns.PowerRules, ref res); AddHistory(res); } { TreeAnalyzer.InvertNegativePowers(ref res); TreeAnalyzer.InvertNegativeMultipliers(ref res); TreeAnalyzer.Sort(ref res, TreeAnalyzer.SortLevel.HIGH_LEVEL); AddHistory(res); #if DEBUG var pre = res.Substitute("x", 3).InnerEval(); #endif res = res.InnerSimplify(); #if DEBUG var post = res.Substitute("x", 3).InnerEval(); if (post != pre && MathS.CanBeEvaluated(res)) { throw new SysException("Wrong inner simplification"); } #endif TreeAnalyzer.ReplaceInPlace(Patterns.CommonRules, ref res); AddHistory(res); TreeAnalyzer.InvertNegativePowers(ref res); } { TreeAnalyzer.InvertNegativePowers(ref res); TreeAnalyzer.ReplaceInPlace(Patterns.DivisionPreparingRules, ref res); res = res.InnerSimplify(); TreeAnalyzer.FindDivisors(ref res, (num, denom) => !MathS.CanBeEvaluated(num) && !MathS.CanBeEvaluated(denom)); } res = res.InnerSimplify(); if (TreeAnalyzer.Optimization.ContainsTrigonometric(res)) { var res1 = res.DeepCopy(); TreeAnalyzer.ReplaceInPlace(Patterns.TrigonometricRules, ref res); AddHistory(res); TreeAnalyzer.ReplaceInPlace(Patterns.ExpandTrigonometricRules, ref res1); AddHistory(res1); res = res.Complexity() > res1.Complexity() ? res1 : res; } if (TreeAnalyzer.Optimization.ContainsPower(res)) { TreeAnalyzer.ReplaceInPlace(Patterns.PowerRules, ref res); AddHistory(res); } AddHistory(res); res = history[history.Keys.Min()][0].DeepCopy(); } if (level > 0) // if level < 0 we don't check whether expanded version is better { var expanded = res.Expand().Simplify(-level); AddHistory(expanded); var collapsed = res.Collapse().Simplify(-level); AddHistory(collapsed); } var result = new Set(); foreach (var pair in history) { foreach (var el in pair.Value) { result.Add(el); } } return(result); }
internal static void ParseMonomial <T>(Entity aVar, Entity expr, out Entity freeMono, ref TreeAnalyzer.IPrimitive <T> power) { if (expr.FindSubtree(aVar) == null) { freeMono = expr; return; } freeMono = 1; // a * b bool allowFloat = typeof(T) == typeof(decimal); foreach (var mp in TreeAnalyzer.LinearChildren(expr, "mulf", "divf", Const.FuncIfMul)) { if (mp.FindSubtree(aVar) == null) { freeMono *= mp; } else if (mp.entType == Entity.EntType.OPERATOR && mp.Name == "powf") { var pow_num = MathS.CanBeEvaluated(mp.Children[1]) ? mp.Children[1].Eval() : mp.Children[1]; // x ^ a is bad if (pow_num.entType != Entity.EntType.NUMBER) { freeMono = null; return; } // x ^ 0.3 is bad if (!allowFloat && !pow_num.Eval().IsInteger()) { freeMono = null; return; } if (mp == aVar) { if (allowFloat) { (power as TreeAnalyzer.PrimitiveDouble).Add(pow_num.GetValue().Real); } else { (power as TreeAnalyzer.PrimitiveInt).Add(pow_num.GetValue().Real.AsInt()); } } else { if (!MathS.CanBeEvaluated(mp.Children[1])) { freeMono = null; return; } Entity tmpFree; // TODO object pow; if (typeof(T) == typeof(decimal)) { pow = new TreeAnalyzer.PrimitiveDouble(); } else { pow = new TreeAnalyzer.PrimitiveInt(); } TreeAnalyzer.IPrimitive <T> q = pow as TreeAnalyzer.IPrimitive <T>; ParseMonomial <T>(aVar, mp.Children[0], out tmpFree, ref q); if (tmpFree == null) { freeMono = null; return; } else { // Can we eval it right here? mp.Children[1] = mp.Children[1].Eval(); freeMono *= MathS.Pow(tmpFree, mp.Children[1]); power.AddMp(q.GetValue(), mp.Children[1].GetValue()); } } } else if (mp == aVar) { if (allowFloat) { (power as TreeAnalyzer.PrimitiveDouble).Add(1); } else { (power as TreeAnalyzer.PrimitiveInt).Add(1); } } else { // a ^ x, (a + x) etc. are bad if (mp.FindSubtree(aVar) != null) { freeMono = null; return; } freeMono *= mp; } } // TODO: do we need to simplify freeMono? }
internal static Set Solve(Entity expr, VariableEntity x) { var childrenRaw = TreeAnalyzer.GatherLinearChildrenOverAndExpand( expr, entity => entity.FindSubtree(x) != null ); if (childrenRaw is null) { return(null); } childrenRaw = childrenRaw.Select(c => c.InnerSimplify()).ToList(); var children = new List <Entity>(); foreach (var child in childrenRaw) { var ch = child; // sqrt(f(x))^2 => f(x) TreeAnalyzer.ReplaceInPlace(Patterns.PowerRules, ref ch); children.Add(ch); } Entity normalPolynom = 0; var fractioned = new List <(Entity multiplier, List <(Entity main, IntegerNumber pow)> fracs)>(); foreach (var child in children) { (Entity multiplier, List <(Entity main, IntegerNumber pow)> fracs)potentialFraction; potentialFraction.multiplier = 1; potentialFraction.fracs = new List <(Entity main, IntegerNumber pow)>(); foreach (var mpChild in TreeAnalyzer.LinearChildrenOverProduct(child)) { if (mpChild.entType != Entity.EntType.OPERATOR) { potentialFraction.multiplier *= mpChild; continue; } if (mpChild.Name != "powf") { potentialFraction.multiplier *= mpChild; continue; } if (mpChild.Children[1].entType != Entity.EntType.NUMBER) { potentialFraction.multiplier *= mpChild; continue; } if (mpChild.Children[0].FindSubtree(x) == null) { potentialFraction.multiplier *= mpChild; continue; } var num = (mpChild.Children[1] as NumberEntity).Value; if (!num.IsRational()) { return(null); // (x + 1)^0.2348728 } if (!num.IsFraction()) // (x + 3) ^ 3 { potentialFraction.multiplier *= mpChild; continue; } var fracNum = num as RationalNumber; var newChild = MathS.Pow(mpChild.Children[0], fracNum.Numerator).InnerSimplify(); var den = fracNum.Denominator; potentialFraction.fracs.Add((newChild, den)); } if (potentialFraction.fracs.Count > 0) { fractioned.Add(potentialFraction); } else { normalPolynom += child; } } if (fractioned.Count == 0) { return(null); // means that one can either be solved polynomially or unsolvable at all } // starting from i = 1 check if all are equal to [0] bool BasesAreEqual(List <(Entity main, IntegerNumber pow)> f1, List <(Entity main, IntegerNumber pow)> f2) { if (f1.Count != f2.Count) { return(false); } for (int i = 0; i < f1.Count; i++) { if (f1[i].main != f2[i].main || f1[i].pow != f2[i].pow) { return(false); } } return(true); } for (int i = 1; i < fractioned.Count; i++) { if (BasesAreEqual(fractioned[i].fracs, fractioned[0].fracs)) { var were = fractioned[0]; fractioned[0] = (were.multiplier + fractioned[i].multiplier, were.fracs); } else { return(null); } } var fractionedProduct = fractioned[0]; var lcm = Utils.LCM(fractionedProduct.fracs.Select( c => c.pow.Value ).ToArray()); var intLcm = Number.Create(lcm); // "-" to compensate sum: x + sqrt(x + 1) = 0 => x = -sqrt(x+1) Entity mp = MathS.Pow(-fractionedProduct.multiplier, intLcm).InnerSimplify(); foreach (var mainPowPair in fractionedProduct.fracs) { mp *= MathS.Pow(mainPowPair.main, Number.Create(lcm.Divide(mainPowPair.pow.Value))); } var finalExpr = MathS.Pow(normalPolynom, intLcm) - mp; return(finalExpr.SolveEquation(x)); }
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)); } } }
/// <summary> /// Multiplies all the given terms and returns the resulting expression /// new Entity[]{ 1, 2, 3 }.MultiplyAll() -> "1 * 2 * 3" /// </summary> public static Entity MultiplyAll(this IEnumerable <Entity> terms) => TreeAnalyzer.MultiHangBinary(terms.ToArray(), (a, b) => a * b);
/// <summary> /// Returns list of unique variables, for example /// it extracts `x`, `goose` from (x + 2 * goose) - pi * x /// </summary> /// <param name="expr"></param> /// <returns></returns> public static Set GetUniqueVariables(Entity expr) => TreeAnalyzer.GetUniqueVariables(expr);
/// <summary> /// Checks tree for some unexpected bad occasions /// Throws SysException's children /// If you need a message, it's better to write /// try /// { /// MathS.CheckTree(a); /// } /// catch (SysException e) /// { /// Console.WriteLine(e.Message); /// } /// </summary> /// <param name="expr"></param> /// <returns></returns> public static void CheckTree(Entity expr) => TreeAnalyzer.CheckTree(expr);
/// <summary> /// solves ax4 + bx3 + cx2 + dx + e /// </summary> /// <param name="a"> /// Coefficient of x^4 /// </param> /// <param name="b"> /// Coefficient of x^3 /// </param> /// <param name="c"> /// Coefficient of x^2 /// </param> /// <param name="d"> /// Coefficient of x /// </param> /// <param name="e"> /// Free coefficient /// </param> /// <returns> /// Set of roots /// </returns> internal static Set SolveQuartic(Entity a, Entity b, Entity c, Entity d, Entity e) { // en: https://en.wikipedia.org/wiki/Quartic_function // ru: https://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D1%82%D0%BE%D0%B4_%D0%A4%D0%B5%D1%80%D1%80%D0%B0%D1%80%D0%B8 Set res; if (TreeAnalyzer.IsZero(e)) { res = SolveCubic(a, b, c, d); res.Add(0); return(res); } if (TreeAnalyzer.IsZero(a)) { return(SolveCubic(b, c, d, e)); } res = new Set(); var alpha = (-3 * MathS.Sqr(b) / (8 * MathS.Sqr(a)) + c / a) .InnerSimplify(); var beta = (MathS.Pow(b, 3) / (8 * MathS.Pow(a, 3)) - (b * c) / (2 * MathS.Sqr(a)) + d / a) .InnerSimplify(); var gamma = (-3 * MathS.Pow(b, 4) / (256 * MathS.Pow(a, 4)) + MathS.Sqr(b) * c / (16 * MathS.Pow(a, 3)) - (b * d) / (4 * MathS.Sqr(a)) + e / a) .InnerSimplify(); if (Const.EvalIfCan(beta) == 0) { res.FastAddingMode = true; for (int s = -1; s <= 1; s += 2) { for (int t = -1; t <= 1; t += 2) { var x = -b / 4 * a + s * MathS.Sqrt((-alpha + t * MathS.Sqrt(MathS.Sqr(alpha) - 4 * gamma)) / 2); res.Add(x); } } res.FastAddingMode = false; return(res); } var oneThird = Number.CreateRational(1, 3); var P = (-MathS.Sqr(alpha) / 12 - gamma) .InnerSimplify(); var Q = (-MathS.Pow(alpha, 3) / 108 + alpha * gamma / 3 - MathS.Sqr(beta) / 8) .InnerSimplify(); var R = -Q / 2 + MathS.Sqrt(MathS.Sqr(Q) / 4 + MathS.Pow(P, 3) / 27); var U = MathS.Pow(R, oneThird) .InnerSimplify(); var y = (Number.CreateRational(-5, 6) * alpha + U + (Const.EvalIfCan(U) == 0 ? -MathS.Pow(Q, oneThird) : -P / (3 * U))) .InnerSimplify(); var W = MathS.Sqrt(alpha + 2 * y) .InnerSimplify(); // Now we need to permutate all four combinations res.FastAddingMode = true; /* we are sure that there's no such root yet */ for (int s = -1; s <= 1; s += 2) { for (int t = -1; t <= 1; t += 2) { var x = -b / (4 * a) + (s * W + t * MathS.Sqrt(-(3 * alpha + 2 * y + s * 2 * beta / W))) / 2; res.Add(x); } } res.FastAddingMode = false; return(res); }
internal override void Check() { // Number has no children TreeAnalyzer.AssertTree(Children.Count == 0, "A number cannot have children"); }
/// <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 Set if successful, /// null otherwise /// </returns> internal static Set SolveAsPolynomial(Entity expr, Entity subtree) { // Here we find all terms expr = expr.Expand(); // (x + 1) * x => x^2 + x List <Entity> children; Set res = new Set(); if (expr.entType == Entity.EntType.OPERATOR && expr.Name == "sumf" || expr.Name == "minusf") { children = TreeAnalyzer.LinearChildren(expr, "sumf", "minusf", Const.FuncIfSum); } else { children = new List <Entity> { expr } }; // Check if all are like {1} * x^n & gather information about them var monomialsByPower = GatherMonomialInformation <long>(children, subtree); if (monomialsByPower == null) { return(null); // meaning that the given equation is not polynomial } Entity GetMonomialByPower(long power) { return(monomialsByPower.ContainsKey(power) ? monomialsByPower[power].InnerSimplify() : 0); } if (ReduceCommonPower(ref monomialsByPower)) // x5 + x3 + x2 - common power is 2, one root is 0, then x3 + x + 1 { res.Add(0); } var powers = new List <long>(monomialsByPower.Keys); var gcdPower = Utils.GCD(powers.ToArray()); // // // // Change all replacements, x6 + x3 + 1 => x2 + x + 1 if (gcdPower != 1) { for (int i = 0; i < powers.Count; i++) { powers[i] /= gcdPower; } var newMonom = new Dictionary <long, Entity>(); foreach (var pair in monomialsByPower) { newMonom[pair.Key / gcdPower] = pair.Value; } monomialsByPower = newMonom; } // // // // 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 Set FinalPostProcess(Set set) { if (gcdPower != 1) { var newSet = new Set(); foreach (var root in set.FiniteSet()) { foreach (var coef in Number.GetAllRoots(1, gcdPower).FiniteSet()) { newSet.Add(coef * MathS.Pow(root, Number.Create(1.0) / gcdPower)); } } set = newSet; } return(set); } if (powers.Count == 0) { return(null); } powers.Sort(); if (powers.Last() == 0) { return(FinalPostProcess(res)); } if (powers.Last() > 4 && powers.Count > 2) { return(null); // So far, we can't solve equations of powers more than 4 } if (powers.Count == 1) { res.Add(0); return(FinalPostProcess(res)); } else if (powers.Count == 2) { // Provided a x ^ n + b = 0 // a = -b x ^ n // (- a / b) ^ (1 / n) = x // x ^ n = (-a / b) var value = (-1 * monomialsByPower[powers[0]] / monomialsByPower[powers[1]]).Simplify(); res.AddRange(TreeAnalyzer.FindInvertExpression(MathS.Pow(subtree, powers[1]), value, subtree)); return(FinalPostProcess(res)); } // 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 else if (powers.Last() == 2) { var a = GetMonomialByPower(2); var b = GetMonomialByPower(1); var c = GetMonomialByPower(0); res.AddRange(SolveQuadratic(a, b, c)); return(FinalPostProcess(res)); } else if (powers.Last() == 3) { var a = GetMonomialByPower(3); var b = GetMonomialByPower(2); var c = GetMonomialByPower(1); var d = GetMonomialByPower(0); res.AddRange(SolveCubic(a, b, c, d)); return(FinalPostProcess(res)); } else if (powers.Last() == 4) { var a = GetMonomialByPower(4); var b = GetMonomialByPower(3); var c = GetMonomialByPower(2); var d = GetMonomialByPower(1); var e = GetMonomialByPower(0); res.AddRange(SolveQuartic(a, b, c, d, e)); return(FinalPostProcess(res)); } return(null); }
/// <summary> /// Expands an equation trying to eliminate all the parentheses ( e. g. 2 * (x + 3) = 2 * x + 2 * 3 ) /// </summary> /// <param name="level"> /// The number of iterations (increase this argument in case if some parentheses remain) /// </param> /// <returns> /// An expanded Entity /// </returns> public Entity Expand(int level) => level <= 1 ? TreeAnalyzer.Replace(Patterns.ExpandRules, this) : TreeAnalyzer.Replace(Patterns.ExpandRules, this).Expand(level - 1);
/// <summary> /// Finds out whether "name" is mentioned at least once /// </summary> /// <param name="name"></param> /// <returns></returns> internal bool ContainsName(string name) => TreeAnalyzer.ContainsName(this, name);
/// <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 Set if successful, /// null otherwise /// </returns> internal static Set SolveAsPolynomial(Entity expr, VariableEntity subtree) { // Safely expand the expression // Here we find all terms /* * var children = new List<Entity>(); * var subNodes = TreeAnalyzer.LinearChildrenOverSum(expr); * foreach (var child in subNodes) * if (child.FindSubtree(subtree) is null) * children.Add(child); // We don't need to expand constants * else * { * * var expanded = TreeAnalyzer.SmartExpandOver(child, entity => entity.FindSubtree(subtree) != null); * if (expanded is null) // Expanded expression is predicted to be too big * return null; * children.AddRange(expanded); * } */ var children = TreeAnalyzer.GatherLinearChildrenOverAndExpand( expr, entity => entity.FindSubtree(subtree) != null ); if (children is null) { return(null); } // // // var res = new Set(); // Check if all are like {1} * x^n & gather information about them var monomialsByPower = GatherMonomialInformation <long>(children, subtree); if (monomialsByPower == null) { return(null); // meaning that the given equation is not polynomial } Entity GetMonomialByPower(long power) { return(monomialsByPower.ContainsKey(power) ? monomialsByPower[power].InnerSimplify() : 0); } if (ReduceCommonPower(ref monomialsByPower)) // x5 + x3 + x2 - common power is 2, one root is 0, then x3 + x + 1 { res.Add(0); } var powers = new List <long>(monomialsByPower.Keys); var gcdPower = Utils.GCD(powers.ToArray()); // // // // Change all replacements, x6 + x3 + 1 => x2 + x + 1 if (gcdPower != 1) { for (int i = 0; i < powers.Count; i++) { powers[i] /= gcdPower; } var newMonom = new Dictionary <long, Entity>(); foreach (var pair in monomialsByPower) { newMonom[pair.Key / gcdPower] = pair.Value; } monomialsByPower = newMonom; } // // // // 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 Set FinalPostProcess(Set set) { if (gcdPower != 1) { var newSet = new Set(); foreach (var root in set.FiniteSet()) { foreach (var coef in Number.GetAllRootsOf1(gcdPower).FiniteSet()) { newSet.Add(coef * MathS.Pow(root, Number.CreateRational(1, gcdPower))); } } set = newSet; } return(set); } if (powers.Count == 0) { return(null); } powers.Sort(); if (powers.Last() == 0) { return(FinalPostProcess(res)); } if (powers.Last() > 4 && powers.Count > 2) { return(null); // So far, we can't solve equations of powers more than 4 } if (powers.Count == 1) { res.Add(0); return(FinalPostProcess(res)); } else if (powers.Count == 2) { // Provided a x ^ n + b = 0 // a = -b x ^ n // (- a / b) ^ (1 / n) = x // x ^ n = (-a / b) var value = (-1 * monomialsByPower[powers[0]] / monomialsByPower[powers[1]]).InnerSimplify(); res.AddRange(TreeAnalyzer.FindInvertExpression(MathS.Pow(subtree, powers[1]), value, subtree)); return(FinalPostProcess(res)); } // 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 else if (powers.Last() == 2) { var a = GetMonomialByPower(2); var b = GetMonomialByPower(1); var c = GetMonomialByPower(0); res.AddRange(SolveQuadratic(a, b, c)); return(FinalPostProcess(res)); } else if (powers.Last() == 3) { var a = GetMonomialByPower(3); var b = GetMonomialByPower(2); var c = GetMonomialByPower(1); var d = GetMonomialByPower(0); res.AddRange(SolveCubic(a, b, c, d)); return(FinalPostProcess(res)); } else if (powers.Last() == 4) { var a = GetMonomialByPower(4); var b = GetMonomialByPower(3); var c = GetMonomialByPower(2); var d = GetMonomialByPower(1); var e = GetMonomialByPower(0); res.AddRange(SolveQuartic(a, b, c, d, e)); return(FinalPostProcess(res)); } return(null); }