def test_pmatch_exponentials(): pat = pm.pat_construct("e^(b)", {"b": "rem"}) pmatch_res, _ = pm.pmatch(pat, p.parse("e^(x)")) assert pmatch_res == {"b": p.parse("x")}
def test_pmatch_exp_with_terms(): pat = pm.pat_construct("x^(a*y)", {"a": "const"}) pmatch_res, _ = pm.pmatch(pat, p.parse("x^(3*y)")) assert pmatch_res == {"a": 3}
def test_pmatch_exp_x_with_coeff(): pat = pm.pat_construct("b*x^a", {"a": "const", "b": "const"}) xyz = p.parse("10*x^3") pmatch_res, _ = pm.pmatch(pat, xyz) assert pmatch_res == {"a": 3, "b": 10}
def test_x_once_coeff(): """Tests pattern matching with input a*x""" # Generate the pattern pat = pm.pat_construct("a*x", {"a": "const"}) pmatch_res, _ = pm.pmatch(pat, p.parse("2*x")) assert pmatch_res == {"a": 2}
def test_recursion_return_part(): pat = pm.pat_construct("b*x+c", {"b": "const", "c": "const"}) pmatch_res, pmatch_res_2 = pm.pmatch(pat, p.parse("x")) assert pmatch_res_2 == p.parse("x")
def test_pmatch_exp(): pat = pm.pat_construct("e^a", {"a": "const"}) pmatch_res, _ = pm.pmatch(pat, p.parse("e^3")) assert pmatch_res == {"a": 3}
def test_pmatch_polyn_one_zero(): pat = pm.pat_construct("a*x^2+b*x+c", {"a": "const", "b": "const", "c": "const"}) pmatch_res, _ = pm.pmatch(pat, p.parse("2*x^2+1")) assert pmatch_res == {"a": 2, "c": 1}
def test_pmatch_fn_args_rem(): pat = pm.pat_construct("ln(a)", {"a": "rem"}) pmatch_res, _ = pm.pmatch(pat, p.parse("ln(x+y)")) assert pmatch_res == {"a": p.parse("x+y")}
def test_pmatch_coeff_sin(): pat = pm.pat_construct("a*sin(b)", {"a": "coeff", "b": "rem"}) pmatch_res, _ = pm.pmatch(pat, p.parse("x*sin(x^2)")) assert pmatch_res == {"a": p.parse("x"), "b": p.parse("x^2")}
def test_pmatch_non_match(coeff): """Tests pattern matching with input a*x+y""" # Generate the pattern pat = pm.pat_construct("a*x", {"a": "const"}) pmatch_res, _ = pm.pmatch(pat, p.parse("{}*x+y".format(coeff))) assert pmatch_res == {}
def test_pmatch_coeff_with_const(): pat = pm.pat_construct("a*b*y/z", {"a": "const", "b":"coeff"}) pmatch_res, _ = pm.pmatch(pat, p.parse("5*sin(x)*y/z")) assert pmatch_res == {"b": p.parse("sin(x)"), "a": 5}
def test_pmatch_coeff(): pat = pm.pat_construct("a*x", {"a": "coeff"}) pmatch_res, _ = pm.pmatch(pat, p.parse("x^2*y*z")) assert pmatch_res == {"a": p.parse("x*y*z")}
def test_pmatch_fn_args_with_pow_non_match(): pat = pm.pat_construct("ln(a*x)^2", {"a": "const"}) pmatch_res, _ = pm.pmatch(pat, p.parse("ln(x)")) assert pmatch_res == {}
def test_pmatch_remaining(): pat = pm.pat_construct("x^a + b", {"a": "const", "b": "rem"}) pmatch_res, _ = pm.pmatch(pat, p.parse("x^3 + y*x")) assert pmatch_res == {"a": 3, "b": p.parse("y*x")}
def test_pmatch_linear_sin_arg(): pat = pm.pat_construct("a*sin(b*x+c)", {"a": "const", "b": "const", "c": "const"}) pmatch_res, _ = pm.pmatch(pat, p.parse("2*sin(3*x+10)")) assert pmatch_res == {"a": 2, "b": 3, "c": 10}
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 test_pmatch_fn_args(): pat = pm.pat_construct("ln(a*x)", {"a": "const"}) pmatch_res, _ = pm.pmatch(pat, p.parse("ln(3*x)")) assert pmatch_res == {"a": 3}