コード例 #1
0
        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));
                }
            }
        }
コード例 #2
0
        // 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);
        }