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)
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": [] }
def unicode_format(self): return "∫({}) d{}".format(ln.latex_numeric_str(self.arg, "unicode"), ln.latex_numeric_str(self.wrt, "unicode"))
def latex_format(self): return "\\int {} \\mathrm{{d}}{}".format( ln.latex_numeric_str(self.arg), self.wrt)
def ascii_format(self): return "{}({},{})".format(self.fname, ln.latex_numeric_str(self.arg, "ascii"), ln.latex_numeric_str(self.wrt, "ascii"))
def unicode_format(self): return "√({})".format(ln.latex_numeric_str(self.arg,"unicode"))
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
def __repr__(self): return "<Function {}, arg {}>".format( self.fname,ln.latex_numeric_str(self.arg) )
def latex_format(self): return "\\sqrt{{{}}}".format(ln.latex_numeric_str(self.arg))
def unicode_format(self): return "{}({})".format(self.fname, ln.latex_numeric_str(self.arg,"unicode"))
def latex_format(self): return "{}({}) ".format(self.latex_fname, ln.latex_numeric_str(self.arg))
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
def latex_format(self): return "\\frac{{\\mathrm{{d}}}}{{\\mathrm{{d}}{}}} ({})".format( self.wrt,ln.latex_numeric_str(self.arg) )
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"))