Esempio n. 1
0
    def int_by_parts(self, u_prime, v):
        """
        Tries to integrate a symbol by parts by splitting the symbol into
        2 parts u_prime and v. So
        integral(u_prime * v) = u * v - integral(u * diff(v))
        where u = integral(u_prime)
        :param u_prime: Numeric object
        :param v: Numeric object
        :return: Numeric object
        """
        # Make it not root integral so we don't get lots of recursion
        int_u_obj = Integrate(u_prime, self.wrt, False)
        u = int_u_obj.eval()
        if not int_u_obj.fully_integrated:
            return None

        diff_v_obj = diff.Differentiate(v, self.wrt)
        v_prime = diff_v_obj.eval()
        if not diff_v_obj.fully_diffed:
            return None
        logger.debug("Int by parts u: {}, v_prime: {}".format(
            ln.latex_numeric_str(u), ln.latex_numeric_str(v_prime)))
        part_to_int = deepcopy(u) * deepcopy(v_prime)
        # Make it not root integral so we don't get lots of recursion
        int_part_obj = Integrate(part_to_int, self.wrt, False)
        int_part_val = int_part_obj.eval()
        if not int_part_obj.fully_integrated:
            return None

        return deepcopy(u) * deepcopy(v) - deepcopy(int_part_val)
Esempio n. 2
0
    def do_execute(self,
                   code,
                   silent,
                   store_history=True,
                   user_expressions=None,
                   allow_stdin=False):
        parser_cls = parser.Parser()
        for line in code.split("\n"):
            try:
                num_result = parser_cls.parse(line)
            except Exception as e:
                self.send_response(self.iopub_socket, 'stream', {
                    "name": "stderr",
                    "text": traceback.format_exc()
                })
                return {
                    "status": "error",
                    "execution_count": self.execution_count,
                    "traceback": [],
                    "ename": "",
                    "evalue": str(e)
                }

            if not silent:
                latex_str = latex_numeric.latex_numeric_str(num_result)
                self.send_response(
                    self.iopub_socket, "display_data", {
                        "data": {
                            "text/latex": "${}$".format(latex_str),
                            "text/plain": latex_str
                        }
                    })

        return {
            "status": "ok",
            "execution_count": self.execution_count,
            "payload": [],
            "user_expressions": []
        }
Esempio n. 3
0
 def unicode_format(self):
     return "∫({}) d{}".format(ln.latex_numeric_str(self.arg, "unicode"),
                               ln.latex_numeric_str(self.wrt, "unicode"))
Esempio n. 4
0
 def latex_format(self):
     return "\\int {} \\mathrm{{d}}{}".format(
         ln.latex_numeric_str(self.arg), self.wrt)
Esempio n. 5
0
 def ascii_format(self):
     return "{}({},{})".format(self.fname,
                               ln.latex_numeric_str(self.arg, "ascii"),
                               ln.latex_numeric_str(self.wrt, "ascii"))
Esempio n. 6
0
 def unicode_format(self):
     return "√({})".format(ln.latex_numeric_str(self.arg,"unicode"))
Esempio n. 7
0
    def eval(self):
        """
        Evaluates the integral. This will be done symbol by symbol, so for
        instance 2*x^2 + sin(x) will be done by integrating 2x^2 then sin(x)
        :return: Numeric object
        """
        logger.debug("Attempting to integrate {}".format(self.arg))
        parser = caspy.parsing.parser.Parser()
        tot = caspy.numeric.numeric.Numeric(0, "number")
        integrated_values = []
        for i, sym in enumerate(self.arg.val):
            # Simplify symbol, this get's rid of issues caused by terms
            # like x^0
            sym.simplify()
            if sym.is_exclusive_numeric():
                # Integrate constant term
                frac_val = sym.sym_frac_eval()
                frac_val_num = caspy.numeric.numeric.Numeric(
                    frac_val, "number")
                wrt_term = caspy.numeric.numeric.Numeric(self.wrt, "sym")
                tot += wrt_term * frac_val_num
                integrated_values.append(i)
                # Go onto next term
                continue

            if not sym.has_variable_in(self.wrt):
                # Integrate term that doesn't contain the variable we're
                # integrating with respect to
                sym_numeric_obj = caspy.numeric.numeric.Numeric(sym, "sym_obj")
                wrt_term = caspy.numeric.numeric.Numeric(self.wrt, "sym")
                tot += sym_numeric_obj * wrt_term
                integrated_values.append(i)
                continue

            # Try matching x^n
            pmatch_res = pm.pmatch_sym("a*{}^n".format(self.wrt), {
                "n": "const",
                "a": "const"
            }, sym, self.parser)
            if pmatch_res != {}:
                logger.debug(
                    "Integrating polynomial term {}, pmatch result {}".format(
                        sym, pmatch_res))
                if pmatch_res["n"] == -1:
                    term_val = self.parser.parse("{} * ln({})".format(
                        copy(pmatch_res["a"]), self.wrt))
                else:
                    # TODO maybe refactor Fraction class so it doesn't return self
                    # so we don't need to do a ridiculous amount of copying like this
                    term_val = self.parser.parse("{} * {} ^ ({})".format(
                        copy(pmatch_res["a"]) / (copy(pmatch_res["n"]) + 1),
                        self.wrt,
                        copy(pmatch_res["n"]) + 1))
                tot += term_val
                integrated_values.append(i)
                # Go onto next term
                continue

            # Try integrating exponential terms
            pmatch_res = pm.pmatch_sym("A1 * e^(A2)".format(self.wrt), {
                "A1": "const",
                "A2": "rem"
            }, sym, self.parser)
            if pmatch_res != {}:
                logger.debug(
                    "Integrating exponential term, pmatch result {}".format(
                        pmatch_res))
                # Currently pattern matching doesn't quite work correctly with matching
                # things like e^(a*x+b) so we will have to pattern match the A2 term
                exp_pow = expand.Expand(pmatch_res["A2"]).eval()
                pat = pm.pat_construct("B1 * {} + B2".format(self.wrt), {
                    "B1": "const",
                    "B2": "rem"
                })
                pmatch_res_pow, _ = pm.pmatch(pat, exp_pow)
                logger.debug("Power pmatch result {}".format(pmatch_res_pow))
                if "B1" in pmatch_res_pow.keys():
                    failed_int = False
                    if "B2" in pmatch_res_pow.keys():
                        if self.wrt in pmatch_res_pow["B2"].get_variables_in():
                            logger.debug(
                                "Failed integration of exponential term")
                            failed_int = True
                    if not failed_int:
                        term_val = caspy.numeric.numeric.Numeric(
                            copy(pmatch_res["A1"]) /
                            copy(pmatch_res_pow["B1"]))
                        term_val.val[0].val.append(
                            ["e", deepcopy(pmatch_res["A2"])])
                        tot += term_val
                        integrated_values.append(i)
                        continue
            # Try integrating exponential terms with u sub
            pmatch_res = pm.pmatch_sym("B1*e^(A1)", {
                "A1": "rem",
                "B1": "coeff"
            }, sym, self.parser)
            if pmatch_res != {}:
                logger.debug(
                    "Integrating e^(...) object with u sub, pmatch_res {}".
                    format(pmatch_res))
                numeric_wrapper = caspy.numeric.numeric.Numeric(
                    deepcopy(sym), "sym_obj")
                u_subbed = self.u_sub_int(pmatch_res["A1"], numeric_wrapper)
                if u_subbed is not None:
                    logger.warning("U sub integral worked")
                    tot += u_subbed
                    integrated_values.append(i)
                    continue

            # Try matching simple sin terms like sin(ax+b)
            pmatch_res = pm.pmatch_sym("a*sin(b*{}+c)".format(self.wrt), {
                "a": "const",
                "b": "const",
                "c": "const"
            }, sym, self.parser)
            if pmatch_res != {}:
                logger.debug("Integrating simple linear sin term")
                term_val = self.parser.parse("{} * cos({}*{}+{})".format(
                    copy(pmatch_res["a"]) * (-1) / copy(pmatch_res["b"]),
                    pmatch_res["b"], self.wrt, pmatch_res.get("c", 0)))
                tot += term_val
                integrated_values.append(i)
                continue

            # Try matching simple sin terms like cos(ax+b)
            pmatch_res = pm.pmatch_sym("a*cos(b*{}+c)".format(self.wrt), {
                "a": "const",
                "b": "const",
                "c": "const"
            }, sym, self.parser)
            if pmatch_res != {}:
                logger.debug("Integrating simple linear cos term")
                term_val = self.parser.parse("{} * sin({}*{}+{})".format(
                    copy(pmatch_res["a"]) / copy(pmatch_res["b"]),
                    pmatch_res["b"], self.wrt, pmatch_res.get("c", 0)))
                tot += term_val
                integrated_values.append(i)
                continue

            # Try integrating sin(___) term with a 'u' substitution
            pmatch_res = pm.pmatch_sym("b*sin(a)", {
                "a": "rem",
                "b": "coeff"
            }, sym, self.parser)
            if pmatch_res != {}:
                logger.debug(
                    "Integrating sin object with u sub, pmatch_res {}".format(
                        pmatch_res))
                numeric_wrapper = caspy.numeric.numeric.Numeric(
                    deepcopy(sym), "sym_obj")
                u_subbed = self.u_sub_int(pmatch_res["a"], numeric_wrapper)
                if u_subbed is not None:
                    logger.warning("U sub integral worked")
                    tot += u_subbed
                    integrated_values.append(i)
                    continue

            # Try integrating cos(___) term with a 'u' substitution
            pmatch_res = pm.pmatch_sym("b*cos(a)", {
                "a": "rem",
                "b": "coeff"
            }, sym, self.parser)
            if pmatch_res != {}:
                logger.debug(
                    "Integrating cos object with u sub, pmatch_res {}".format(
                        pmatch_res))
                numeric_wrapper = caspy.numeric.numeric.Numeric(
                    deepcopy(sym), "sym_obj")
                u_subbed = self.u_sub_int(pmatch_res["a"], numeric_wrapper)
                if u_subbed is not None:
                    logger.warning("U sub integral worked")
                    tot += u_subbed
                    integrated_values.append(i)
                    continue

            if self.root_integral and not self.dont_expand:
                # Try expanding the symbol then integrating
                sym_numeric = caspy.numeric.numeric.Numeric(
                    deepcopy(sym), "sym_obj")
                expand_obj = expand.Expand(sym_numeric)
                expanded = expand_obj.eval()
                logger.info("Expanded val: {}".format(
                    ln.latex_numeric_str(expanded)))
                integ_exp = Integrate(expanded, self.wrt, True, True)
                new_integral = integ_exp.eval()
                if integ_exp.fully_integrated:
                    integrated_values.append(i)
                    tot += new_integral
                    continue
                else:
                    logger.info("Failed expanded int {}".format(
                        ln.latex_numeric_str(new_integral)))

            if not self.root_integral:
                continue
            # Try integrating by parts
            int_done_by_parts = False
            for (a, b) in group_list_into_all_poss_pairs(deepcopy(sym.val)):
                # Make symbols for a and b\
                a_sym = caspy.numeric.symbol.Symbol(1, Fraction(1, 1))
                a_sym.val = a
                a_num = caspy.numeric.numeric.Numeric(a_sym, "sym_obj")
                b_sym = caspy.numeric.symbol.Symbol(1, Fraction(1, 1))
                b_sym.val = b
                b_num = caspy.numeric.numeric.Numeric(b_sym, "sym_obj")
                logger.debug("PRE Int by parts u_prime {} v {}".format(
                    ln.latex_numeric_str(a_num), ln.latex_numeric_str(b_num)))
                by_parts_ab = self.int_by_parts(a_num, b_num)
                if by_parts_ab is not None:
                    logger.debug("Int by parts u_prime {} v {}".format(
                        ln.latex_numeric_str(a_num),
                        ln.latex_numeric_str(b_num)))
                    tot += by_parts_ab
                    integrated_values.append(i)
                    int_done_by_parts = True
                else:
                    by_part_ba = self.int_by_parts(b_num, a_num)
                    if by_part_ba is not None:
                        logger.debug("Int by parts2 u_prime {} v {}".format(
                            ln.latex_numeric_str(b_num),
                            ln.latex_numeric_str(a_num)))
                        tot += by_part_ba
                        integrated_values.append(i)
                        int_done_by_parts = True

                if int_done_by_parts:
                    break
                else:
                    logger.debug("One round of int by parts failed")

            if int_done_by_parts:
                continue

        new_val = []
        # Remove integrated values from the arg property
        for j, term_val in enumerate(self.arg.val):
            if j not in integrated_values:
                new_val.append(term_val)

        if new_val == []:
            # All terms have been integrated
            self.fully_integrated = True
            return tot
        else:
            # Make a numeric object to store the non integrated terms
            new_val_numeric_obj = caspy.numeric.numeric.Numeric(1, "number")
            new_val_numeric_obj.val = new_val
            self.arg = new_val_numeric_obj
            # Return integrated terms and other terms in an integral
            remaining_int = super().eval()
            return remaining_int + tot
Esempio n. 8
0
 def __repr__(self):
     return "<Function {}, arg {}>".format(
         self.fname,ln.latex_numeric_str(self.arg)
     )
Esempio n. 9
0
 def latex_format(self):
     return "\\sqrt{{{}}}".format(ln.latex_numeric_str(self.arg))
Esempio n. 10
0
 def unicode_format(self):
     return "{}({})".format(self.fname, ln.latex_numeric_str(self.arg,"unicode"))
Esempio n. 11
0
 def latex_format(self):
     return "{}({}) ".format(self.latex_fname, ln.latex_numeric_str(self.arg))
Esempio n. 12
0
    def eval(self):
        """
        Evaluates the derivative symbol by symbol
        :return: Numeric object
        """
        logger.debug("Attempting to differentiate {} wrt {}".format(self.arg,
                                                                    self.wrt))

        tot = caspy.numeric.numeric.Numeric(0, "number")
        diffed_values = []
        for i,sym in enumerate(self.arg.val):
            sym.simplify()
            if sym.is_exclusive_numeric() or not sym.has_variable_in(self.wrt):
                # Derivative of constant term is zero
                diffed_values.append(i)
                continue

            # Try matching x^n
            pmatch_res = pm.pmatch_sym("a*{}^n".format(self.wrt),
                                       {"n": "const", "a": "const"}, sym, self.parser)
            if pmatch_res != {}:
                term_val = self.parser.parse("{} * {} ^ ({})".format(
                    copy(pmatch_res["a"]) * copy(pmatch_res["n"]),
                    self.wrt,
                    copy(pmatch_res["n"]) - 1
                ))
                tot += term_val
                diffed_values.append(i)
                continue

            # Try diffing sin terms
            pmatch_res = pm.pmatch_sym("A1*sin(A2)",
                                       {"A1": "const", "A2": "rem"}, sym, self.parser)
            if pmatch_res != {}:
                logger.debug("Differentiating sin term")
                # Diff the argument
                d_obj = Differentiate(pmatch_res["A2"],self.wrt)
                derivative = d_obj.eval()
                if d_obj.fully_diffed:
                    derivative.simplify()
                    numeric_coeff = caspy.numeric.numeric.Numeric(
                        pmatch_res["A1"], "number"
                    )
                    cos_obj = caspy.numeric.numeric.Numeric(
                        trig.Cos(pmatch_res["A2"]), "sym"
                    )
                    term_val = numeric_coeff * derivative * cos_obj
                    tot += term_val
                    diffed_values.append(i)
                    continue

            # Try diffing cos terms
            pmatch_res = pm.pmatch_sym("A1*cos(A2)",
                                       {"A1": "const", "A2": "rem"}, sym, self.parser)
            if pmatch_res != {}:
                logger.debug("Differentiating cos term")
                # Diff the argument
                d_obj = Differentiate(pmatch_res["A2"],self.wrt)
                derivative = d_obj.eval()
                if d_obj.fully_diffed:
                    derivative.simplify()
                    numeric_coeff = caspy.numeric.numeric.Numeric(
                        pmatch_res["A1"], "number"
                    ).neg()
                    cos_obj = caspy.numeric.numeric.Numeric(
                        trig.Sin(pmatch_res["A2"]), "sym"
                    )
                    term_val = numeric_coeff * derivative * cos_obj
                    tot += term_val
                    diffed_values.append(i)
                    continue

            # Try diffing ln terms
            pmatch_res = pm.pmatch_sym("A1*ln(A2)",
                                       {"A1": "const", "A2": "rem"}, sym, self.parser)
            if pmatch_res != {}:
                logger.debug("Differentiating ln term")
                # Diff the argument
                d_obj = Differentiate(deepcopy(pmatch_res["A2"]),self.wrt)
                derivative = d_obj.eval()
                if d_obj.fully_diffed:
                    derivative.simplify()
                    numeric_coeff = caspy.numeric.numeric.Numeric(
                        pmatch_res["A1"], "number"
                    )
                    term_val = numeric_coeff * derivative / deepcopy(pmatch_res["A2"])
                    tot += term_val
                    diffed_values.append(i)
                    continue

            # Try differentiating exponential terms
            pmatch_res = pm.pmatch_sym("A1 * e^(A2)".format(self.wrt),
                                    {"A1": "const", "A2": "rem"}, sym, self.parser)
            if pmatch_res != {}:
                logger.debug("Differentiating exponential term, pmatch result {}".format(pmatch_res))
                # Currently pattern matching doesn't quite work correctly with matching
                # things like e^(a*x+b) so we will have to pattern match the A2 term
                exp_pow = expand.Expand(deepcopy(pmatch_res["A2"])).eval()
                d_obj = Differentiate(exp_pow, self.wrt)
                derivative = d_obj.eval()
                if d_obj.fully_diffed:
                    derivative.simplify()
                    term_val = caspy.numeric.numeric.Numeric(copy(pmatch_res["A1"]))
                    term_val.val[0].val.append(["e",pmatch_res["A2"]])
                    term_val *= derivative
                    tot += term_val
                    diffed_values.append(i)
                    continue

            if self.root_diff and not self.dont_expand:
                sym_numeric = caspy.numeric.numeric.Numeric(deepcopy(sym), "sym_obj")
                expand_obj = expand.Expand(sym_numeric)
                expanded = expand_obj.eval()
                logger.info("Expanded val: {}".format(ln.latex_numeric_str(expanded)))
                diff_exp = Differentiate(expanded, self.wrt, True, True)
                new_integral = diff_exp.eval()
                if diff_exp.fully_diffed:
                    diffed_values.append(i)
                    tot += new_integral
                    continue
                else:
                    logger.info("Failed expanded diff {}".format(ln.latex_numeric_str(new_integral)))

            if not self.root_diff:
                continue
            diffed_by_parts = False
            # Try differentiation of products
            for (a,b) in group_list_into_all_poss_pairs(deepcopy(sym.val)):
                derivatives = []
                non_derivatives = []
                diff_prod_pair_fail = False
                for part in [a,b]:
                    # Generate numeric objects for it
                    part_num = caspy.numeric.numeric.Numeric(0)
                    part_num.val[0].val = deepcopy(part)
                    part_diff_obj = Differentiate(part_num,self.wrt,False)
                    part_derivative = part_diff_obj.eval()
                    if part_diff_obj.fully_diffed:
                        derivatives.append(part_derivative.simplify())
                        non_derivatives.append(part_num)
                    else:
                        diff_prod_pair_fail = True
                        break
                if diff_prod_pair_fail:
                    continue
                # We have differentiaed the products
                term_val = deepcopy(derivatives[0]) * deepcopy(non_derivatives[1]) + \
                           deepcopy(derivatives[1]) * deepcopy(non_derivatives[0])
                tot += term_val
                diffed_values.append(i)
                diffed_by_parts = True
                break
            if diffed_by_parts:
                continue


        new_val = []
        # Remove integrated values from the arg property
        for j, term_val in enumerate(self.arg.val):
            if j not in diffed_values:
                new_val.append(term_val)

        if new_val == []:
            # All terms have been integrated
            self.fully_diffed = True
            return tot
        else:
            # Make a numeric object to store the non integrated terms
            new_val_numeric_obj = caspy.numeric.numeric.Numeric(1, "number")
            new_val_numeric_obj.val = new_val
            self.arg = new_val_numeric_obj
            # Return integrated terms and other terms in an integral
            remaining_int = super().eval()
            return remaining_int + tot
Esempio n. 13
0
 def latex_format(self):
     return "\\frac{{\\mathrm{{d}}}}{{\\mathrm{{d}}{}}} ({})".format(
         self.wrt,ln.latex_numeric_str(self.arg)
     )
Esempio n. 14
0
def main(args):

    a_parser = argparse.ArgumentParser(description="""
Caspy - A CAS built in Python

Available functions in the system:
- integrate(f(x),x) : Integrate a function with respect to a variable x
- diff(f(x),x) : Differentiates a function with respect to a variable x
- factor(f(x)) : Factorises a polynomial g(x) using Kronecker's algorithm
- expand_trig(...) : Expands a trigonometric expression, for instance
                      sin(2x) is expanded as 2sin(x)cos(x)
- expand(...) : Expands brackets in an expression 
- re(...) : Returns floating point answer where possible, for instance
            re(sqrt(2)) gives 1.4142...
    """,formatter_class=RawTextHelpFormatter)
    a_parser.add_argument("--timer",action="store_true",default=False,
                        help="Time execution of statements")
    a_parser.add_argument("--verbose",action="store_true",default=False,
                        help="Enable verbose logging")
    a_parser.add_argument("--debug",action="store_true",default=False,
                        help="Enable more verbose logging for debugging purposes")
    a_parser.add_argument("--ascii",action="store_true",default=False,
                          help="Output string using ASCII characters")
    a_parser.add_argument("--latex",action="store_true",default=False,
                          help="Output representation of string in LaTeX form")
    a_parser.add_argument("--unicode", action="store_true", default=False,
                          help="Output string using Unicode characters")

    args_results = a_parser.parse_args(args)
    log_level = logging.ERROR
    if args_results.debug:
        log_level = logging.DEBUG
    elif args_results.verbose:
        log_level = logging.WARNING


    logging_config = {
        "version": 1,
        "disable_existing_loggers": False,
        "formatters": {
            "main": {"format": "%(levelname)s-%(name)s-%(lineno)d: %(message)s"}
        },
        "handlers": {
            "numeric": {
                "class": "logging.StreamHandler",
                "formatter": "main",
                "level": log_level},
            "functions": {
                "class": "logging.StreamHandler",
                "formatter": "main",
                "level": log_level},
            "cutelog": {
                "class": "logging.handlers.SocketHandler",
                "host": "127.0.0.1",
                "port": "19996"
            }
        },
        "loggers": {
            "": {
                "handlers": ["numeric", "cutelog"],
                "level": log_level
            },
            "caspy.numeric": {
                "handlers": ["numeric", "cutelog"],
                "level": log_level
            },
            "caspy.pattern_match": {
                "handlers": ["functions", "cutelog"],
                "level": log_level
            },
            "caspy.functions": {
                "handlers": ["functions", "cutelog"],
                "level": log_level
            }
        }
    }

    logging.config.dictConfig(logging_config)
    logger = logging.getLogger(__name__)

    parser_cls = parser.Parser(output="ASCII")
    # parser_cls.parse("factor(3x^9+x^3+10x^2)")
    # return None
    if not args_results.ascii:
        args_results.ascii = not (args_results.latex or args_results.unicode)
    while True:
        line = input(">> ")
        try:
            start = timer()
            out = parser_cls.parse(line)
            end = timer()
            if args_results.timer:
                print("Time elapsed: {}s".format(end-start))
        except ZeroDivisionError:
            print("Math Error: Division by zero")
            continue
        if args_results.verbose or args_results.debug:
            print("Parser output = {}".format(out))
        if args_results.ascii:
            print(latex_numeric_str(out,"ascii"))
        if args_results.latex:
            print(latex_numeric_str(out,"latex"))
        if args_results.unicode:
            print(latex_numeric_str(out, "unicode"))