From 1154a9a3937b15e7367b878c310225a6210a4635 Mon Sep 17 00:00:00 2001 From: Laura Gao Date: Tue, 10 Jun 2025 14:23:46 -0400 Subject: [PATCH 01/10] fix sectan bug --- src/simpy/simplify/simplify.py | 13 ++++++++++--- tests/test_trig_simplify.py | 13 +++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/simpy/simplify/simplify.py b/src/simpy/simplify/simplify.py index 2960f29..f13f086 100644 --- a/src/simpy/simplify/simplify.py +++ b/src/simpy/simplify/simplify.py @@ -128,6 +128,10 @@ def sin_cos_condition(expr: Sum): rest = result["rest"] return factor * perform(inner) + rest + # right now we are doing the sec/tan simplification in a separate function because its implementation is a bit more + # complex? + # but i feel like maybe ideally it should be in this function along with cos^2(x) + sin^2(x) = 1 and we can have + # them both done together with more advanced regex. but for now, this is fine! # other_table = [ # (r"^sec\((.+)\)\^2$", r"^-tan\((.+)\)\^2$", Const(1)), # ] @@ -247,7 +251,7 @@ def reciprocate_trigs(expr: Expr, **kwargs) -> Expr: def simplify(expr: Expr) -> Expr: """Simplifies an expression. - This is the general one that does all heuristics & is for aesthetics (& comparisons). + This is the general simplification function that does all heuristics & is for aesthetics (& comparisons). Use more specific simplification functions in integration please. """ if expr.expandable(): @@ -342,7 +346,7 @@ def perform(e: Power): for s in secs: new = replace_factory(condition, perform)(s) - new = new.expand() if new.expandable() else s + new = new.expand() if new.expandable() else new assert isinstance(new, Sum) tans.extend(new.terms) @@ -351,9 +355,12 @@ def perform(e: Power): # This must mean that we simplified the sum into one term return new_sum - if len(new_sum.terms) < sum.terms: + if is_simpler(new_sum, sum): return new_sum + # if we didn't simplify, return original + return sum + def is_simpler(e1, e2) -> bool: """returns whether e1 is simpler than e2""" diff --git a/tests/test_trig_simplify.py b/tests/test_trig_simplify.py index 50257b5..ecfcdcf 100644 --- a/tests/test_trig_simplify.py +++ b/tests/test_trig_simplify.py @@ -68,6 +68,19 @@ def test_pts(): assert_simplified(e1, e2) +def test_sectan_simple(): + expr = sec(x) ** 2 - tan(x) ** 2 + assert_simplified(expr, 1) + + +def test_sectan_plus(): + # this one shouldn't be simplified to 1 + # but it can be rewritten to 1 + 2 * tan(x) ** 2 + # the fact that we replace sec^2(x) with tan^2(x) + 1 instead of the other way around is kinda arbitrary. + expr2 = sec(x) ** 2 + tan(x) ** 2 + assert_simplified(expr2, 1 + 2 * tan(x) ** 2) + + def test_sectan(): # need to replace sec^2(x) with tan^2(x) + 1 e1 = 1 / (4 * cos(x) ** 4) - 1 / (3 * cos(x) ** 6) + 1 / (8 * cos(x) ** 8) From d243b425ebd240353e9d415bffca7883123c46d1 Mon Sep 17 00:00:00 2001 From: Laura Gao Date: Tue, 10 Jun 2025 14:54:23 -0400 Subject: [PATCH 02/10] make any_constant :D --- src/simpy/expr.py | 6 ++++-- src/simpy/regex.py | 37 +++++++++++++++++++++++++++++++++++-- tests/test_regex.py | 25 ++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/src/simpy/expr.py b/src/simpy/expr.py index 0f8311c..131de76 100644 --- a/src/simpy/expr.py +++ b/src/simpy/expr.py @@ -326,9 +326,11 @@ def _nesting_without_factor(expr: "Expr") -> int: else: expr2 = remove_const_factor(expr) - if isinstance(expr2, (Symbol, Any_)): + if isinstance(expr2, Symbol) or isinstance(expr2, Any_) and not expr2.is_constant: ans = 1 - elif len(expr2.symbols()) == 0 and len(get_anys(expr2)) == 0: + elif len(expr2.symbols()) == 0 and ( + len(get_anys(expr2)) == 0 or all([a.is_constant for a in get_anys(expr2)]) + ): ans = 0 else: ans = 1 + max(_nesting_without_factor(sub_expr) for sub_expr in expr2.children()) diff --git a/src/simpy/regex.py b/src/simpy/regex.py index e7480c0..1c4bed3 100644 --- a/src/simpy/regex.py +++ b/src/simpy/regex.py @@ -8,17 +8,30 @@ from dataclasses import dataclass, fields from typing import Any, Callable, Dict, Iterable, List, Literal, Optional, Tuple, Type -from .expr import Expr, Power, Prod, Rat, SingleFunc, Sum, Symbol, cast, log +from .expr import Expr, Num, Power, Prod, Rat, SingleFunc, Sum, Symbol, cast, log from .utils import ExprCondition, ExprFn, OptionalExprFn, random_id class Any_(Expr): _fields_already_casted = True - def __init__(self, key=None, *, is_multiple_terms=False): + def __init__( + self, key: str = None, condition: Callable[[Expr], bool] = None, *, is_constant=False, is_multiple_terms=False + ): + """ + key: unique identifier of each Any_ instance. + this is used for if we want each Any_ object match to be the same thing. + condition: a condition for if sth matches an Any_? by default it's nothing. + is_constant: a decorator actually only for sorting purposes. if True, this Any_ object will be considered a + constant for sorting purposes. for matching purposes, pls specify in the condition callable. + """ if not key: key = random_id(10) + if condition is None: + condition = lambda e: True # default condition is always true self._key = key + self._condition = condition + self._is_constant = is_constant self._is_multiple_terms = is_multiple_terms super().__post_init__() @@ -26,6 +39,14 @@ def __init__(self, key=None, *, is_multiple_terms=False): def key(self) -> str: return self._key + @property + def condition(self) -> Callable[[Expr], bool]: + return self._condition + + @property + def is_constant(self) -> bool: + return self._is_constant + @property def is_multiple_terms(self) -> bool: return self._is_multiple_terms @@ -59,6 +80,9 @@ def latex(self) -> str: any_ = Any_() +any_constant = Any_( + key="constant", condition=lambda e: isinstance(e, Num), is_constant=True +) # matches any numerical constant. # smallTODO: make this a namedtuple EqResult = Dict[Literal["success", "factor", "rest", "matches"], Any] @@ -253,6 +277,11 @@ def _is_sum_eq(expr: Sum, query: Sum) -> EqResult: return self._result def _eq(self, expr: Any, query: Any) -> bool: + """Recursively checks if `expr` matches `query` one-for-one, no up to factors/sums. + This method is the recursed component. + """ + + ## base cases ## if isinstance(expr, list): if not (isinstance(query, list) and len(expr) == len(query)): return False @@ -262,6 +291,8 @@ def _eq(self, expr: Any, query: Any) -> bool: if not isinstance(expr, Expr) or not isinstance(query, Expr): return False if isinstance(query, Any_): + if not query.condition(expr): + return False self._matches[query.key].append(expr) return True if not query.has(Any_): @@ -292,6 +323,8 @@ def _eq(self, expr: Any, query: Any) -> bool: if not expr.__class__ == query.__class__: return False + + ## and here we recurse. ## return all(self._eq(getattr(expr, field.name), getattr(query, field.name)) for field in fields(expr)) diff --git a/tests/test_regex.py b/tests/test_regex.py index 8a745cd..2426c50 100644 --- a/tests/test_regex.py +++ b/tests/test_regex.py @@ -2,7 +2,7 @@ from test_utils import x, y from simpy.expr import * -from simpy.regex import Any_, any_, eq +from simpy.regex import Any_, any_, any_constant, eq def test_any_basic(): @@ -95,3 +95,26 @@ def test_cofounder(): query = -sin(any_) ** 2 + 1 out = eq(expr, query, up_to_factor=True, up_to_sum=True) assert out["success"] is False + + +def test_any_constant(): + # Tests that any_constant matches constants. + expr = 2 * x + 3 + query = 2 * x + any_constant + out = eq(expr, query) + assert out["success"] + assert out["matches"] == 3 + + expr = 2 * x + 5 * y + query = 2 * x + any_constant * y + out = eq(expr, query) + assert out["success"] + assert out["matches"] == 5 + + +def test_any_constant_fail(): + # Tests that any_constant does not match variables. + expr = 2 * x + y + query = 2 * x + any_constant + out = eq(expr, query) + assert not out["success"] From 5704dd5074aa0047c4c1646df939dfd5cb6d7524 Mon Sep 17 00:00:00 2001 From: Laura Gao Date: Tue, 10 Jun 2025 22:55:17 -0400 Subject: [PATCH 03/10] fix simplify expanding constant bug --- src/simpy/simplify/simplify.py | 14 ++++++++------ tests/test_simplify.py | 9 +++++++++ 2 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 tests/test_simplify.py diff --git a/src/simpy/simplify/simplify.py b/src/simpy/simplify/simplify.py index f13f086..73ccc02 100644 --- a/src/simpy/simplify/simplify.py +++ b/src/simpy/simplify/simplify.py @@ -15,6 +15,7 @@ cot, csc, log, + nesting, sec, sin, tan, @@ -366,9 +367,10 @@ def is_simpler(e1, e2) -> bool: """returns whether e1 is simpler than e2""" c1 = count_symbols(e1) c2 = count_symbols(e2) - return c1 < c2 - # if c1 < c2: - # return True - # if c1 == c2: - # return len(repr(e1)) < len(repr(e2)) - # return False + if c1 < c2: + return True + + if c1 == c2: + return nesting(e1) < nesting(e2) + + return False diff --git a/tests/test_simplify.py b/tests/test_simplify.py new file mode 100644 index 0000000..8313884 --- /dev/null +++ b/tests/test_simplify.py @@ -0,0 +1,9 @@ +from test_utils import assert_eq_strict, x + +from simpy.expr import * +from simpy.simplify import simplify + + +def test_simplifies_when_expanding_is_simpler(): + expr = 2 * (2 * x + 3) + assert_eq_strict(simplify(expr), 4 * x + 6) From da268f245551c312e6c0fabb13ba4bab6442ba9a Mon Sep 17 00:00:00 2001 From: Laura Gao Date: Tue, 10 Jun 2025 23:49:31 -0400 Subject: [PATCH 04/10] commit the logging output! it's a cool chart / deliverable. --- benchmark/benchmark-profiler.py | 2 +- benchmark/integration_log.png | Bin 0 -> 53279 bytes benchmark/integration_log.txt | 93 ++++++++++++++++++++++++++++++++ benchmark/readme.md | 4 ++ src/simpy/debug/logger.py | 38 ++++++++++++- 5 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 benchmark/integration_log.png create mode 100644 benchmark/integration_log.txt diff --git a/benchmark/benchmark-profiler.py b/benchmark/benchmark-profiler.py index 1bfabe3..5b341b5 100644 --- a/benchmark/benchmark-profiler.py +++ b/benchmark/benchmark-profiler.py @@ -14,7 +14,7 @@ BENCHMARKING_SUITE = [ -5 * x**4 / (1 - x**2) ** Rat(5, 2), x**2 / sqrt(1 - x**3), - (Rat(1, 15) - Rat(1, 360) * (x - 6)) * (1 - (40 - x) ** 2 / 875), + # (Rat(1, 15) - Rat(1, 360) * (x - 6)) * (1 - (40 - x) ** 2 / 875), # looks too ugly on the plot cos(w * x - phi) * cos(w * x), sin(2 * x) / cos(2 * x), cos(x) ** 2, diff --git a/benchmark/integration_log.png b/benchmark/integration_log.png new file mode 100644 index 0000000000000000000000000000000000000000..35545bee342ea13a9119f052a7de3fff0f019eae GIT binary patch literal 53279 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 literal 0 HcmV?d00001 diff --git a/benchmark/integration_log.txt b/benchmark/integration_log.txt new file mode 100644 index 0000000..a08798f --- /dev/null +++ b/benchmark/integration_log.txt @@ -0,0 +1,93 @@ +Integrand: time taken (s) + +sec(x)^4*tan(x)^5: 0.16937255859375 +-5*x^4/(-x^2 + 1)^(5/2): 0.13976669311523438 +5*csc(x)^2: 0.11545586585998535 +cos(2*x)*sin(2*x)*sin(x): 0.06932449340820312 +sec(2*x)*tan(2*x): 0.0646052360534668 +sin(pi*x)*x^2: 0.05409359931945801 +asin(x): 0.053441524505615234 +1/sqrt(-x^2 + 10*x + 11): 0.052265167236328125 +2*cot(x)*csc(x): 0.05167746543884277 +tan(x)^4: 0.04779386520385742 +sin(x)^2*cos(x)^3: 0.0475003719329834 +cos(w*x)*cos(w*x - phi): 0.04714822769165039 +sin(x)^5: 0.04366707801818848 +x^2/sqrt(-x^3 + 1): 0.04044771194458008 +e^x/(e^x + 1): 0.03517651557922363 +x*e^(-x): 0.03151559829711914 +sin(2*x)/cos(2*x): 0.02821207046508789 +ln(x + 6)/x^2: 0.02619481086730957 +cos(x)^2: 0.02358555793762207 +cos(w*x)*sin(w*x): 0.023393869400024414 +sin(x)^2: 0.020847082138061523 +x*cos(x): 0.019170522689819336 +(2*x - 5)^10: 0.0037734508514404297 +(x + 8)/(x*(x + 6)): 0.0003879070281982422 +(x - 5)/(-2*x + 2): 0.0002715587615966797 +6*e^x: 0.0002617835998535156 + + + +Solution tree of the integral with most time spent: +[0] {4} sec(x)^4*tan(x)^5 1/(4*cos(x)^4) - 1/(3*cos(x)^6) + 1/(8*cos(x)^8) (NoneType) (solved) +[1] {4} sin(x)^5/cos(x)^9 1/(4*cos(x)^4) - 1/(3*cos(x)^6) + 1/(8*cos(x)^8) (RewriteTrig) (solved) +[2] {6} sin(x)*(-cos(x)^2 + 1)^2/cos(x)^9 1/(4*cos(x)^4) - 1/(3*cos(x)^6) + 1/(8*cos(x)^8) (RewritePythagorean) (solved) +[3] {5} sin(x)/cos(x)^9 + sin(x)/cos(x)^5 - 2*sin(x)/co... 1/(4*cos(x)^4) - 1/(3*cos(x)^6) + 1/(8*cos(x)^8) (Expand) (solved) +[4] {4} sin(x)/cos(x)^9 1/(8*cos(x)^8) (Additivity) (solved) +[5] {4} filler 1/(8*cos(x)^8) (ByParts) (SOLUTION) + +[5] {2} -1/(u_2)^9 1/(8*(u_2)^8) (GenericUSub) (solved) +[6] {2} filler 1/(8*(u_2)^8) (ByParts) (SOLUTION) + +[4] {4} sin(x)/cos(x)^5 1/(4*cos(x)^4) (Additivity) (solved) +[5] {4} filler 1/(4*cos(x)^4) (ByParts) (SOLUTION) + +[5] {2} -1/(u_3)^5 1/(4*(u_3)^4) (GenericUSub) (solved) +[6] {2} filler 1/(4*(u_3)^4) (ByParts) (SOLUTION) + +[4] {4} -2*sin(x)/cos(x)^7 -1/(3*cos(x)^6) (Additivity) (solved) +[5] {4} sin(x)/cos(x)^7 1/(6*cos(x)^6) (PullConstant) (solved) +[6] {4} filler 1/(6*cos(x)^6) (ByParts) (SOLUTION) + + + + +[0] {4} sec(x)^4*tan(x)^5 (NoneType) (solved) +[1] {4} sin(x)^5/cos(x)^9 (RewriteTrig) (solved) +[2] {6} sin(x)*(-cos(x)^2 + 1)^2/cos(x)^9 (RewritePythagorean) (solved) +[3] {5} sin(x)/cos(x)^9 + sin(x)/cos(x)^5 - 2*sin(x)/co... (Expand) (solved) +[4] {4} sin(x)/cos(x)^9 (Additivity) (solved) +[5] {4} filler (ByParts) (SOLUTION) + +[5] {4} csc(x)^8*tan(x)^9 (RewriteTrig) (stale) (UNSET) + +[5] {4} sec(x)^8/cot(x) (RewriteTrig) (stale) (UNSET) + +[5] {2} -1/(u_2)^9 (GenericUSub) (solved) +[6] {2} filler (ByParts) (SOLUTION) + +[4] {4} sin(x)/cos(x)^5 (Additivity) (solved) +[5] {4} filler (ByParts) (SOLUTION) + +[5] {4} csc(x)^4*tan(x)^5 (RewriteTrig) (stale) (UNSET) + +[5] {4} sec(x)^4/cot(x) (RewriteTrig) (stale) (UNSET) + +[5] {2} -1/(u_3)^5 (GenericUSub) (solved) +[6] {2} filler (ByParts) (SOLUTION) + +[4] {4} -2*sin(x)/cos(x)^7 (Additivity) (solved) +[5] {4} sin(x)/cos(x)^7 (PullConstant) (solved) +[6] {4} filler (ByParts) (SOLUTION) + +[6] {4} csc(x)^6*tan(x)^7 (RewriteTrig) (stale) (UNSET) + +[6] {4} sec(x)^6/cot(x) (RewriteTrig) (stale) (UNSET) + +[6] {2} -1/(u_4)^7 (GenericUSub) (stale) (UNSET) + +[1] {4} csc(x)^4*tan(x)^9 (RewriteTrig) (stale) (UNSET) + +[1] {4} sec(x)^4/cot(x)^5 (RewriteTrig) (stale) (UNSET) + diff --git a/benchmark/readme.md b/benchmark/readme.md index 857d57a..e7d4808 100644 --- a/benchmark/readme.md +++ b/benchmark/readme.md @@ -20,3 +20,7 @@ The output would look something like this: 142 0.000 0.000 0.374 0.003 integration.py:147(_cycle) ``` + +## Logging + +`benchmark-profiler.py` also keeps track of the time it takes for each integral to run and creates a plot of it. See `integration_log.png` for the bar chart; see `integration_log.txt` for some more detailed numerical breakdowns. diff --git a/src/simpy/debug/logger.py b/src/simpy/debug/logger.py index 064c0a8..2d28140 100644 --- a/src/simpy/debug/logger.py +++ b/src/simpy/debug/logger.py @@ -1,3 +1,28 @@ +"""See benchmark/benchmark-profiler.py for an example usage of the logger. + +``` +import simpy as sp +from simpy.debug.logger import Logger +from simpy.integration import Integration + +logger = Logger() +Integration.logger = logger + +# do some integration as normal... +x = sp.symbols('x') +sp.integrate(x ** 2) + +logger.dump() # dumps information into integration_log.txt +logger.plot() # creates a bar chart of integrals speeds to integration_log.png + +``` + +Yeah, this is kind of jank, mutating the whole integration classs... What can I say, this whole debug module is jank. + +TODO: make some consistent organizational decisions. Maybe move benchmark to this folder or move logger to +benchmark. Probably the former. Maybe BENCHMARK_SUITE can be exported from the debug module. +""" + import time from typing import Dict, NamedTuple @@ -25,6 +50,12 @@ def __init__(self): self._data = {} def log(self, expr: Expr, time_spent: float, root: Node): + """Log an integration entry. + + expr: integrand + time_spent: time taken to integrate expr, in seconds + root: the root node of the integration tree + """ self._data[str(expr)] = Datum(expr, time_spent, root) @property @@ -39,12 +70,14 @@ def dump(self): self.sort() with open("integration_log.txt", "w") as f: + f.write("Integrand: time taken (s)") + f.write("\n\n") for k, v in self._data.items(): f.write(f"{k}: {v.time_spent}\n") # For the one with the most time spent, print the tree. f.write("\n\n\n") - f.write("Most time spent: \n") + f.write("Solution tree of the integral with most time spent: \n") print_solution_tree(self._data[list(self._data.keys())[0]].root, func=lambda x: f.write(f"{x}\n")) f.write("\n\n\n") print_tree(self._data[list(self._data.keys())[0]].root, func=lambda x: f.write(f"{x}\n")) @@ -56,6 +89,9 @@ def plot(self): x = list(self._data.keys()) y = [v.time_spent for v in self._data.values()] plt.bar(x, y) + plt.ylabel("Time taken to integrate (s)") + plt.xticks(rotation=90) # rotate labels vertically + plt.tight_layout() # automatically adjust spacing (needed to show the entirety of the vertical labels) plt.savefig("integration_log.png") From cb6410a8184468d9e043ecda88594f9ac9288762 Mon Sep 17 00:00:00 2001 From: Laura Gao Date: Tue, 10 Jun 2025 23:51:06 -0400 Subject: [PATCH 05/10] rerun logger output with laptop on performance mode --- benchmark/integration_log.png | Bin 53279 -> 53587 bytes benchmark/integration_log.txt | 52 +++++++++++++++++----------------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/benchmark/integration_log.png b/benchmark/integration_log.png index 35545bee342ea13a9119f052a7de3fff0f019eae..11841cb0e41d530c48fe439112a433f0338e3d30 100644 GIT binary patch literal 53587 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 diff --git a/benchmark/integration_log.txt b/benchmark/integration_log.txt index a08798f..05e1751 100644 --- a/benchmark/integration_log.txt +++ b/benchmark/integration_log.txt @@ -1,31 +1,31 @@ Integrand: time taken (s) -sec(x)^4*tan(x)^5: 0.16937255859375 --5*x^4/(-x^2 + 1)^(5/2): 0.13976669311523438 -5*csc(x)^2: 0.11545586585998535 -cos(2*x)*sin(2*x)*sin(x): 0.06932449340820312 -sec(2*x)*tan(2*x): 0.0646052360534668 -sin(pi*x)*x^2: 0.05409359931945801 -asin(x): 0.053441524505615234 -1/sqrt(-x^2 + 10*x + 11): 0.052265167236328125 -2*cot(x)*csc(x): 0.05167746543884277 -tan(x)^4: 0.04779386520385742 -sin(x)^2*cos(x)^3: 0.0475003719329834 -cos(w*x)*cos(w*x - phi): 0.04714822769165039 -sin(x)^5: 0.04366707801818848 -x^2/sqrt(-x^3 + 1): 0.04044771194458008 -e^x/(e^x + 1): 0.03517651557922363 -x*e^(-x): 0.03151559829711914 -sin(2*x)/cos(2*x): 0.02821207046508789 -ln(x + 6)/x^2: 0.02619481086730957 -cos(x)^2: 0.02358555793762207 -cos(w*x)*sin(w*x): 0.023393869400024414 -sin(x)^2: 0.020847082138061523 -x*cos(x): 0.019170522689819336 -(2*x - 5)^10: 0.0037734508514404297 -(x + 8)/(x*(x + 6)): 0.0003879070281982422 -(x - 5)/(-2*x + 2): 0.0002715587615966797 -6*e^x: 0.0002617835998535156 +sec(x)^4*tan(x)^5: 0.07898354530334473 +-5*x^4/(-x^2 + 1)^(5/2): 0.06334471702575684 +5*csc(x)^2: 0.0379023551940918 +cos(2*x)*sin(2*x)*sin(x): 0.03209376335144043 +sec(2*x)*tan(2*x): 0.03049325942993164 +2*cot(x)*csc(x): 0.02516341209411621 +1/sqrt(-x^2 + 10*x + 11): 0.02431011199951172 +sin(pi*x)*x^2: 0.02408909797668457 +tan(x)^4: 0.02292919158935547 +sin(x)^2*cos(x)^3: 0.022572040557861328 +cos(w*x)*cos(w*x - phi): 0.02143096923828125 +sin(x)^5: 0.019292116165161133 +x^2/sqrt(-x^3 + 1): 0.01819753646850586 +asin(x): 0.016515731811523438 +e^x/(e^x + 1): 0.01630997657775879 +sin(2*x)/cos(2*x): 0.013149738311767578 +ln(x + 6)/x^2: 0.012115240097045898 +x*e^(-x): 0.011050939559936523 +cos(w*x)*sin(w*x): 0.010923147201538086 +cos(x)^2: 0.010917901992797852 +sin(x)^2: 0.008718252182006836 +x*cos(x): 0.00684356689453125 +(2*x - 5)^10: 0.00171661376953125 +(x - 5)/(-2*x + 2): 0.00013136863708496094 +(x + 8)/(x*(x + 6)): 0.00010347366333007812 +6*e^x: 8.034706115722656e-05 From 703968b69982339d98f3465cdc6ad548625c16c2 Mon Sep 17 00:00:00 2001 From: Laura Gao Date: Tue, 10 Jun 2025 23:58:31 -0400 Subject: [PATCH 06/10] quickfixes --- src/simpy/regex.py | 3 --- tests/test_transforms.py | 1 + tests/test_utils.py | 8 ++++---- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/simpy/regex.py b/src/simpy/regex.py index 1c4bed3..c704b8f 100644 --- a/src/simpy/regex.py +++ b/src/simpy/regex.py @@ -54,9 +54,6 @@ def is_multiple_terms(self) -> bool: def __eq__(self, other): if isinstance(other, Any_): return self.key == other.key - # if isinstance(other, Expr): - # return True - # return NotImplemented return False def __repr__(self) -> str: diff --git a/tests/test_transforms.py b/tests/test_transforms.py index 60b23b8..e5ede10 100644 --- a/tests/test_transforms.py +++ b/tests/test_transforms.py @@ -45,6 +45,7 @@ def test_polynomial_division(): tr.forward(test_node) ans = test_node.children[0].expr + assert_eq_strict(ans, -x + x / (-(x**2) + 1)) def test_complete_the_square(): diff --git a/tests/test_utils.py b/tests/test_utils.py index f984e67..1201171 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -35,14 +35,14 @@ def _assert_eq_plusc(a, b, *vars) -> Tuple[bool, Expr]: diff = a - b if len(diff.symbols()) == 0 or vars and all(var not in diff.symbols() for var in vars): return True, None - diff = simplify_to_same_standard(diff) + diff = _simplify_to_same_standard(diff) if not vars: return len(diff.symbols()) == 0, diff else: return all(var not in diff.symbols() for var in vars), diff -def simplify_to_same_standard(expr: Expr) -> Expr: +def _simplify_to_same_standard(expr: Expr) -> Expr: if expr.expandable(): expr2 = expr.expand() else: @@ -62,8 +62,8 @@ def assert_eq_value(a: Expr, b: Expr): """Tests that the values of a & b are the same in spirit, regardless of how they are represented with the Expr data structures.""" if a == b: return - diff = simplify_to_same_standard(a - b) - assert diff == 0, f"a != b, {simplify_to_same_standard(a)} != {simplify_to_same_standard(b)}" + diff = _simplify_to_same_standard(a - b) + assert diff == 0, f"a != b, {_simplify_to_same_standard(a)} != {_simplify_to_same_standard(b)}" @cast From ec15d4428568dec09fb8d1d4d2894043dd72ce2e Mon Sep 17 00:00:00 2001 From: Laura Gao Date: Wed, 11 Jun 2025 00:01:03 -0400 Subject: [PATCH 07/10] basic case funky-trig-substitution integral works! epic --- src/simpy/expr.py | 5 ++ src/simpy/regex.py | 32 ++++++++++++- src/simpy/transforms.py | 71 ++++++++++++++++++++++++++-- tests/test_khan_academy_integrals.py | 8 ++++ tests/test_regex.py | 30 +++++++++++- 5 files changed, 139 insertions(+), 7 deletions(-) diff --git a/src/simpy/expr.py b/src/simpy/expr.py index 131de76..49a2fc6 100644 --- a/src/simpy/expr.py +++ b/src/simpy/expr.py @@ -228,6 +228,11 @@ def evalf(self, subs: Optional[Dict[str, "Expr"]] = None) -> "Expr": def children(self) -> List["Expr"]: raise NotImplementedError(f"Cannot get children of {self.__class__.__name__}") + @property + def childless(self) -> bool: + """Returns True if it's a basic element like a symbol or a number that doesn't have any subcomponents at all.""" + return len(self.children()) == 0 + def contains(self: "Expr", var: "Symbol") -> bool: is_var = isinstance(self, Symbol) and self.name == var.name return is_var or any(e.contains(var) for e in self.children()) diff --git a/src/simpy/regex.py b/src/simpy/regex.py index c704b8f..0274b69 100644 --- a/src/simpy/regex.py +++ b/src/simpy/regex.py @@ -1,7 +1,11 @@ -"""Custom library for checking what shit exists. Replaces searching the repr with regex. +"""Custom library for checking what exists within exprs. Replaces searching the repr with regex. This module is currently still developmental. It does the job often but is not promised to be robust outside of the cases it is currently used for. Use with caution. + +TODO: write a regex quickstart guide. Maybe spin this off into a subfolder and make the outward facing API more +intuitive. Perhaps make it similar to the regex library. ++ Unit testing with comprehensively thought-out cases. """ from collections import defaultdict @@ -395,6 +399,28 @@ def count(expr: Expr, query: Expr) -> int: return sum(count(e, query) for e in expr.children()) +def contains(expr: Expr, query: Expr) -> EqResult: + """Checks if `query` appears in `expr`. Assumes query contains Any_ objects. + Exact any-matches only, no up to factor or sum. + Returns a results dictionary like eq() does. with `success` and `matches` keys. + """ + ## base cases ## + eq_output = eq(expr, query) + if eq_output["success"]: + return eq_output + if expr.childless: + return {"success": False} + + ## recursive cases ## + # this isn't super sophisticated for dupes but it's fine for now. + for e in expr.children(): + eq_output_ = contains(e, query) + if eq_output_["success"]: + return eq_output_ + + return {"success": False} + + def contains_cls(expr: Expr, cls: Type[Expr]) -> bool: if isinstance(expr, cls): return True @@ -412,6 +438,10 @@ def general_count(expr: Expr, condition: ExprCondition) -> int: def general_contains(expr: Expr, condition: ExprCondition) -> bool: + """contains with a condition function instead of any. + + this is sorta-legacy --- i think we should use contains instead; it's cuter. any-matches are cute. + """ if condition(expr): return True return any(general_contains(e, condition) for e in expr.children()) diff --git a/src/simpy/transforms.py b/src/simpy/transforms.py index b1347b3..f27637c 100644 --- a/src/simpy/transforms.py +++ b/src/simpy/transforms.py @@ -33,7 +33,7 @@ from .integral_table import check_integral_table from .linalg import invert from .polynomial import Polynomial, is_polynomial, polynomial_to_expr, rid_ending_zeros, to_const_polynomial -from .regex import count, general_contains, replace, replace_class, replace_factory +from .regex import Any_, contains, count, general_contains, replace, replace_class, replace_factory from .simplify import pythagorean_simplification from .simplify.product_to_sum import product_to_sum_unit from .utils import ExprFn, eq_with_var, random_id @@ -211,6 +211,7 @@ def check(self, node: Node) -> bool: class USub(Transform, ABC): """Base class for u-substituion transforms.""" + # u (new var) written in terms of x (old var) _u: Expr = None def backward(self, node: Node) -> None: @@ -377,8 +378,6 @@ def _get_last_heuristic_transform(node: Node, tup=(PullConstant, Additivity)): return node.transform -# Let's just add all the transforms we've used for now. -# and we will make this shit good and generalized later. class TrigUSub2(USub): """ u-sub of a trig function @@ -410,8 +409,8 @@ def check(self, node: Node) -> bool: if super().check(node) is False: return False - # Since B and C essentially undo each other, we want to make sure that the last - # heuristic transform wasn't C. + # Since TrigUSub2 and InverseTrigUSub essentially undo each other, we want to make sure that the last + # heuristic transform wasn't InverseTrigUSub. t = _get_last_heuristic_transform(node) if isinstance(t, InverseTrigUSub): @@ -966,6 +965,67 @@ def forward(self, node: Node) -> None: self._u = self._u +class TrigUSub(USub): + """This is the substitution of the form x = 4*cos(theta) (TODO: write better descrip later)""" + + _a: Rat = None # constant in the square root + _exponent: Rat = None # exponent of the square root (includes the square root, is a fraction w denom = 2) + _case: int = None # 0 for sqrt(a^2 - x^2), 1 for sqrt(a^2 + x^2), 2 for sqrt(-a^2 + x^2) + + def check(self, node: Node): + """Check if node.expr contains sqrt(a^2-x^2) or sqrt(a^2+x^2) where a is a constant.""" + if super().check(node) is False: + return False + + # check if any instance of sqrt(a^2 - x^2) or sqrt(a^2 + x^2) or sqrt(-a^2 + x^2) appears. + def squared_integer_condition(expr: Expr) -> bool: + return isinstance(expr, Rat) and isinstance(sqrt(expr), Rat) + + a_squared = Any_("squared_integer", squared_integer_condition, is_constant=True) + any_square_root_exponent = Any_( + "square_root_exponent", lambda expr: isinstance(expr, Rat) and expr.denominator == 2, is_constant=True + ) + queries = [ + (a_squared - node.var**2) ** any_square_root_exponent, + (node.var**2 - a_squared) ** any_square_root_exponent, + (node.var**2 + a_squared) ** any_square_root_exponent, + ] + for i, query in enumerate(queries): + out = contains(node.expr, query) + if out["success"]: + self._a = sqrt(out["matches"]["squared_integer"]) + self._exponent = out["matches"]["square_root_exponent"] + self._case = i + return True + + return False + + def forward(self, node: Node) -> None: + if self._case == 0: + # in the case of sqrt(a^2 - x^2): + # x = a * sin(theta) + # dx = a * cos(theta) d(theta) + theta = generate_intermediate_var() + dx_dtheta = self._a * cos(theta) + + # so we replace x = a * sin(theta), this effectively leads to + # replacing sqrt(a^2 - x^2) = a * cos^2(theta) + theta_expr = replace( + node.expr, + (self._a**2 - node.var**2) ** self._exponent, + (self._a * cos(theta)) ** self._exponent.numerator, + ) + if theta_expr.contains(node.var): + theta_expr = replace(theta_expr, node.var, self._a * sin(theta)) + + new_integrand = theta_expr * dx_dtheta + node.add_child(Node(new_integrand, theta, self, node)) + + self._u = asin(node.var / self._a) # theta in terms of x + + return NotImplemented + + class CompleteTheSquare(Transform): """Integration via completing the square""" @@ -1120,6 +1180,7 @@ def backward(self, node: Node) -> None: RewriteTrig, RewritePythagorean, InverseTrigUSub, + TrigUSub, CompleteTheSquare, GenericUSub, ] diff --git a/tests/test_khan_academy_integrals.py b/tests/test_khan_academy_integrals.py index 201af52..786b90b 100644 --- a/tests/test_khan_academy_integrals.py +++ b/tests/test_khan_academy_integrals.py @@ -202,3 +202,11 @@ def test_more_complicated_trig(): expr = tan(x) ** 5 * sec(x) ** 4 expected_ans = tan(x) ** 6 / 6 + tan(x) ** 8 / 8 assert_integral(expr, expected_ans) + + +def test_trigonometric_substitution(): + # that's this one: https://www.khanacademy.org/math/integral-calculus/ic-integration/ic-trig-substitution/e/integration-using-trigonometric-substitution + expr = (4 - x**2) ** Fraction(3, 2) + expected_ans = sin(4 * asin(x / 2)) / 2 + 4 * sin(2 * asin(x / 2)) + 6 * asin(x / 2) # TODO: simplify this + ans = integrate(expr) + assert_eq_plusc(expected_ans, ans) diff --git a/tests/test_regex.py b/tests/test_regex.py index 2426c50..b6b9fd5 100644 --- a/tests/test_regex.py +++ b/tests/test_regex.py @@ -2,7 +2,7 @@ from test_utils import x, y from simpy.expr import * -from simpy.regex import Any_, any_, any_constant, eq +from simpy.regex import Any_, any_, any_constant, contains, eq def test_any_basic(): @@ -18,6 +18,14 @@ def test_sort_anys(): assert eq(sin(x) * sec(x), sec(any_) * sin(any_)) +def test_eq_with_different_anys(): + any2 = Any_() + expr = sin(x) + cos(y) + query = sin(any_) + cos(any2) + out = eq(expr, query) + assert out["success"] + + @pytest.mark.parametrize( ["sum", "expected"], [ @@ -118,3 +126,23 @@ def test_any_constant_fail(): query = 2 * x + any_constant out = eq(expr, query) assert not out["success"] + + +def test_any_constant_with_multiple_anys(): + expr = 2 * x + 3 + query = 2 * any_ + any_constant + out = eq(expr, query) + assert out["success"] + + +def test_contains(): + query = log(sin(any_) ** 2 + cos(any_) ** 2) + expr = (log(sin(x) ** 2 + cos(x) ** 2) + 3) ** 2 + assert contains(expr, query)["success"] + assert contains(expr, query)["matches"] == x + + +def test_contains_fail(): + query = log(sin(any_) ** 2 + cos(any_) ** 2) + expr = (log(sin(x) ** 2 + cos(x) ** 3) + 3) ** 2 + 1 + assert not contains(expr, query)["success"] From 5e56a49f87a6bd0214e0f52c1d0557d1a46d9ff9 Mon Sep 17 00:00:00 2001 From: Laura Gao Date: Wed, 11 Jun 2025 23:58:55 -0400 Subject: [PATCH 08/10] incomplete double angle simplification // mvp, works for our trig sub test case --- .gitignore | 2 ++ src/simpy/regex.py | 9 +++-- src/simpy/simplify/product_to_sum.py | 50 +++++++++++++++++++++++++++- src/simpy/simplify/simplify.py | 27 ++++++--------- src/simpy/simplify/utils.py | 15 +++++++++ tests/test_khan_academy_integrals.py | 2 +- 6 files changed, 84 insertions(+), 21 deletions(-) create mode 100644 src/simpy/simplify/utils.py diff --git a/.gitignore b/.gitignore index 8844369..374a379 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ venv*/ coverage.xml sandbox/ +# working benchmark directory +b/ # from pip install -e . *.egg-info/ diff --git a/src/simpy/regex.py b/src/simpy/regex.py index 0274b69..c59b686 100644 --- a/src/simpy/regex.py +++ b/src/simpy/regex.py @@ -299,6 +299,8 @@ def _eq(self, expr: Any, query: Any) -> bool: if not query.has(Any_): return False + # ok bro idk why i did this i kinda hate it. + # like what if we just check if both query and expr are products and have special handling for that? if not self._is_divide: # You don't get to divide if we already is --- prevents inf recursion. one, quotient_matches = divide_anys(query, expr) @@ -349,8 +351,8 @@ def _make_factors_list(expr: Expr) -> List[Expr]: anys.append(t) else: terms.append(t) - if len([t for t in expr.terms if isinstance(t, Any_)]) > 1: - raise NotImplementedError(f"{expr} is ambiguous") + # if len([t for t in expr.terms if isinstance(t, Any_)]) > 1: + # raise NotImplementedError(f"{expr} is ambiguous") if len(anys) > 0: terms.extend(anys) if len(any_factors) > 0: @@ -360,6 +362,9 @@ def _make_factors_list(expr: Expr) -> List[Expr]: numfactors = _make_factors_list(num) denfactors = _make_factors_list(denom) matches = defaultdict(list) + + # For every factor in the numerator, try to find a matching factor in the denominator. + # If a match is found, remove both factors from their respective lists. And add the match to the matches dict. for i in range(len(numfactors)): f = numfactors[i] for j in range(len(denfactors)): diff --git a/src/simpy/simplify/product_to_sum.py b/src/simpy/simplify/product_to_sum.py index 26d81e0..d9af5e7 100644 --- a/src/simpy/simplify/product_to_sum.py +++ b/src/simpy/simplify/product_to_sum.py @@ -1,7 +1,9 @@ from typing import List, Optional, Union -from ..expr import Expr, Power, Prod, Rat, Sum, cos, remove_const_factor, sin +from ..expr import Expr, Power, Prod, Rat, Sum, TrigFunctionNotInverse, cos, nesting, remove_const_factor, sin +from ..regex import Any_, any_, eq from ..utils import count_symbols +from .utils import is_simpler def _perform_on_terms( @@ -137,3 +139,49 @@ def product_to_sum(expr: Expr) -> Optional[Expr]: if len(final.terms) == len(expr.terms) and count_symbols(final) < count_symbols(expr): # This ensures that e.g. 2*cos(x)*sin(2*x)/3 - cos(2*x)*sin(x)/3 simplifies to -2*sin(x)**3/3 + sin(x) return final + + +def double_angle(expr: Expr) -> Optional[Expr]: + """Applies double angle + Used in simplify + + Assumes that expr.has(TrigFunctionNotInverse) == True + """ + + if not isinstance(expr, (sin, cos)): + return + + any_even_number = Any_( + "even_number", lambda expr: isinstance(expr, Rat) and expr.denominator == 1 and expr % 2 == 0, is_constant=True + ) + query = any_even_number * any_ + out = eq(expr.inner, query) + + if not out["success"]: + return + + x = out["matches"][any_.key] + num = out["matches"]["even_number"] + + if isinstance(expr, sin): + if num == 2: + final = 2 * sin(x) * cos(x) + elif num == 4: + final = 4 * sin(x) * cos(x) - 8 * sin(x) ** 3 * cos(x) + else: + return + # raise NotImplementedError("Double angle for sin with num > 4 is not implemented") + else: + return + # raise NotImplementedError("Double angle for cos is not implemented") + + if not final.has(TrigFunctionNotInverse) or is_simpler(final, expr): + # If final doesn't have any trig functions, it's definitely simpler. + # this can def be ... improved lol. + # currently im basing it off of the + # sin(4*asin(x/2)) -> 2*x*sqrt(-x^2/4 + 1) - x^3*sqrt(-x^2/4 + 1) + # case. + # like that shit is not simpler by any other metric other than it doesn't have the sin(asin) nesting yk. + # nesting of 2 trig funcs is always ugllyyyyyy. maybe the most robust metric should just rid those ugly + # nests. + return final diff --git a/src/simpy/simplify/simplify.py b/src/simpy/simplify/simplify.py index 73ccc02..7db2208 100644 --- a/src/simpy/simplify/simplify.py +++ b/src/simpy/simplify/simplify.py @@ -21,8 +21,9 @@ tan, ) from ..regex import any_, eq, general_contains, kinder_replace, kinder_replace_many, replace_class, replace_factory -from ..utils import ExprFn, count_symbols -from .product_to_sum import product_to_sum +from ..utils import ExprFn +from .product_to_sum import double_angle, product_to_sum +from .utils import is_simpler def expand_logs(expr: Expr, **kwargs) -> Expr: @@ -275,7 +276,7 @@ def simplify(expr: Expr) -> Expr: return expr -def trig_simplify(expr): +def trig_simplify(expr: Expr) -> Tuple[Expr, bool]: # reciprocate and combine trigs is last because sometimes the pythag complex simplification will # generate new trigs in the num/denom that can be simplified down. expr, is_hit_1 = kinder_replace_many( @@ -286,10 +287,15 @@ def trig_simplify(expr): ) expr, is_hit_2 = kinder_replace_many( expr, - [_combine_trigs, product_to_sum, sectan], + [_combine_trigs, product_to_sum, double_angle, sectan], overarching_cond=lambda x: x.has(TrigFunctionNotInverse), verbose=True, ) + # sometimes, the double angle will change a non-sum to a sum, so we need to check again for possible expanding. + if is_hit_2 and expr.expandable(): + # q: should we check that the expanded stuff is simpler? for now im not doing that. + # because it is a bit expensive and we can do that later if we want. + expr = expr.expand() return expr, is_hit_1 or is_hit_2 @@ -361,16 +367,3 @@ def perform(e: Power): # if we didn't simplify, return original return sum - - -def is_simpler(e1, e2) -> bool: - """returns whether e1 is simpler than e2""" - c1 = count_symbols(e1) - c2 = count_symbols(e2) - if c1 < c2: - return True - - if c1 == c2: - return nesting(e1) < nesting(e2) - - return False diff --git a/src/simpy/simplify/utils.py b/src/simpy/simplify/utils.py new file mode 100644 index 0000000..73907ad --- /dev/null +++ b/src/simpy/simplify/utils.py @@ -0,0 +1,15 @@ +from ..expr import nesting +from ..utils import count_symbols + + +def is_simpler(e1, e2) -> bool: + """returns whether e1 is simpler than e2""" + c1 = count_symbols(e1) + c2 = count_symbols(e2) + if c1 < c2: + return True + + if c1 == c2: + return nesting(e1) < nesting(e2) + + return False diff --git a/tests/test_khan_academy_integrals.py b/tests/test_khan_academy_integrals.py index 786b90b..d9669af 100644 --- a/tests/test_khan_academy_integrals.py +++ b/tests/test_khan_academy_integrals.py @@ -207,6 +207,6 @@ def test_more_complicated_trig(): def test_trigonometric_substitution(): # that's this one: https://www.khanacademy.org/math/integral-calculus/ic-integration/ic-trig-substitution/e/integration-using-trigonometric-substitution expr = (4 - x**2) ** Fraction(3, 2) - expected_ans = sin(4 * asin(x / 2)) / 2 + 4 * sin(2 * asin(x / 2)) + 6 * asin(x / 2) # TODO: simplify this + expected_ans = 5 * x * sqrt(1 - x**2 / 4) - x**3 * sqrt(1 - x**2 / 4) / 2 + 6 * asin(x / 2) ans = integrate(expr) assert_eq_plusc(expected_ans, ans) From bd5d66d3a2b6976e70124c5dfba8ff13e0f7095c Mon Sep 17 00:00:00 2001 From: Laura Gao Date: Thu, 4 Sep 2025 17:28:10 -0400 Subject: [PATCH 09/10] finish trigsub transform for two other cases and add test for one of them --- src/simpy/transforms.py | 67 ++++++++++++++++++++++------ tests/test_khan_academy_integrals.py | 7 +++ 2 files changed, 60 insertions(+), 14 deletions(-) diff --git a/src/simpy/transforms.py b/src/simpy/transforms.py index f27637c..94b0762 100644 --- a/src/simpy/transforms.py +++ b/src/simpy/transforms.py @@ -17,6 +17,7 @@ Symbol, TrigFunction, TrigFunctionNotInverse, + acos, asin, atan, cos, @@ -965,19 +966,29 @@ def forward(self, node: Node) -> None: self._u = self._u +asec = lambda x: acos(1 / x) + + class TrigUSub(USub): - """This is the substitution of the form x = 4*cos(theta) (TODO: write better descrip later)""" + """This is the substitution of the form x = 4*cos(theta) + + If the integrand contains sqrt(a^2 - x^2), sqrt(a^2 + x^2), or sqrt(-a^2 + x^2), + you can do this trig sub. + 3 cases: + 1. sqrt(a^2 - x^2): x = a*sin(theta) + 2. sqrt(a^2 + x^2): x = a*tan(theta) + 3. sqrt(-a^2 + x^2): x = a*sec(theta) + """ _a: Rat = None # constant in the square root _exponent: Rat = None # exponent of the square root (includes the square root, is a fraction w denom = 2) _case: int = None # 0 for sqrt(a^2 - x^2), 1 for sqrt(a^2 + x^2), 2 for sqrt(-a^2 + x^2) def check(self, node: Node): - """Check if node.expr contains sqrt(a^2-x^2) or sqrt(a^2+x^2) where a is a constant.""" + """Check if node.expr contains sqrt(a^2 - x^2) or sqrt(a^2 + x^2) or sqrt(-a^2 + x^2) where a is a constant.""" if super().check(node) is False: return False - # check if any instance of sqrt(a^2 - x^2) or sqrt(a^2 + x^2) or sqrt(-a^2 + x^2) appears. def squared_integer_condition(expr: Expr) -> bool: return isinstance(expr, Rat) and isinstance(sqrt(expr), Rat) @@ -987,8 +998,8 @@ def squared_integer_condition(expr: Expr) -> bool: ) queries = [ (a_squared - node.var**2) ** any_square_root_exponent, - (node.var**2 - a_squared) ** any_square_root_exponent, (node.var**2 + a_squared) ** any_square_root_exponent, + (node.var**2 - a_squared) ** any_square_root_exponent, ] for i, query in enumerate(queries): out = contains(node.expr, query) @@ -1001,29 +1012,57 @@ def squared_integer_condition(expr: Expr) -> bool: return False def forward(self, node: Node) -> None: + theta = generate_intermediate_var() + a = self._a + x = node.var if self._case == 0: # in the case of sqrt(a^2 - x^2): # x = a * sin(theta) # dx = a * cos(theta) d(theta) - theta = generate_intermediate_var() - dx_dtheta = self._a * cos(theta) + dx_dtheta = a * cos(theta) # so we replace x = a * sin(theta), this effectively leads to # replacing sqrt(a^2 - x^2) = a * cos^2(theta) theta_expr = replace( node.expr, - (self._a**2 - node.var**2) ** self._exponent, - (self._a * cos(theta)) ** self._exponent.numerator, + (a**2 - x**2) ** self._exponent, + (a * cos(theta)) ** self._exponent.numerator, ) - if theta_expr.contains(node.var): - theta_expr = replace(theta_expr, node.var, self._a * sin(theta)) + if theta_expr.contains(x): + theta_expr = replace(theta_expr, x, a * sin(theta)) + self._u = asin(x / a) # theta in terms of x - new_integrand = theta_expr * dx_dtheta - node.add_child(Node(new_integrand, theta, self, node)) + elif self._case == 1: + # in th cas of sqrt(a^2 + x^2) + # x = a * tan(theta) + # dx = a * sec(theta) ** 2 * d(theta) + dx_dtheta = self._a * sec(theta) ** 2 - self._u = asin(node.var / self._a) # theta in terms of x + # sqrt(a^2 + x^2) = a * sec(theta) + theta_expr = replace( + node.expr, + (a**2 + x**2) ** self._exponent, + (a * sec(theta)) ** self._exponent.numerator, + ) + if theta_expr.contains(node.var): + theta_expr = replace(theta_expr, x, a * tan(theta)) + self._u = atan(x / a) # theta in terms of x + + else: + # x = a * sec(theta) + dx_dtheta = a * sec(theta) * tan(theta) + # sqrt(a^2 + x^2) = a * tan(theta) + theta_expr = replace( + node.expr, + (-(a**2) + x**2) ** self._exponent, + (a * tan(theta)) ** self._exponent.numerator, + ) + if theta_expr.contains(x): + theta_expr = replace(theta_expr, x, a * sec(theta)) + self._u = asec(x / a) # theta in terms of x - return NotImplemented + new_integrand = theta_expr * dx_dtheta + node.add_child(Node(new_integrand, theta, self, node)) class CompleteTheSquare(Transform): diff --git a/tests/test_khan_academy_integrals.py b/tests/test_khan_academy_integrals.py index d9669af..6406dc8 100644 --- a/tests/test_khan_academy_integrals.py +++ b/tests/test_khan_academy_integrals.py @@ -210,3 +210,10 @@ def test_trigonometric_substitution(): expected_ans = 5 * x * sqrt(1 - x**2 / 4) - x**3 * sqrt(1 - x**2 / 4) / 2 + 6 * asin(x / 2) ans = integrate(expr) assert_eq_plusc(expected_ans, ans) + + +def test_trigonometric_substitution_tan_sub(): + expr = 1 / (x**2 + 4) ** Fraction(3, 2) + ans = integrate(expr) + expected_ans = x / (8 * sqrt(x**2 / 4 + 1)) + assert_eq_plusc(expected_ans, ans) From 7af2601f39f48843cf3610ce8d0e3eeddd03ca8c Mon Sep 17 00:00:00 2001 From: Laura Gao Date: Thu, 4 Sep 2025 17:38:08 -0400 Subject: [PATCH 10/10] add test for the sec replace case of trigusub --- src/simpy/expr.py | 2 +- src/simpy/simplify/product_to_sum.py | 3 +++ tests/test_khan_academy_integrals.py | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/simpy/expr.py b/src/simpy/expr.py index 49a2fc6..b8e5e27 100644 --- a/src/simpy/expr.py +++ b/src/simpy/expr.py @@ -2072,7 +2072,7 @@ def symbols(symbols: str) -> Union[Symbol, List[Symbol]]: @cast -def diff(expr: Expr, var: Optional[Symbol]) -> Expr: +def diff(expr: Expr, var: Optional[Symbol] = None) -> Expr: """Takes the derivative of expr relative to var. If expr has only one symbol in it, var doesn't need to be specified.""" if not hasattr(expr, "diff"): raise NotImplementedError(f"Differentiation of {expr} not implemented") diff --git a/src/simpy/simplify/product_to_sum.py b/src/simpy/simplify/product_to_sum.py index d9af5e7..984a2bb 100644 --- a/src/simpy/simplify/product_to_sum.py +++ b/src/simpy/simplify/product_to_sum.py @@ -164,10 +164,13 @@ def double_angle(expr: Expr) -> Optional[Expr]: num = out["matches"]["even_number"] if isinstance(expr, sin): + # TODO: make this robust through iteration or recursion if num == 2: final = 2 * sin(x) * cos(x) elif num == 4: final = 4 * sin(x) * cos(x) - 8 * sin(x) ** 3 * cos(x) + elif num == 6: + final = 6 * sin(x) * cos(x) - 32 * sin(x) ** 3 * cos(x) + 32 * sin(x) ** 5 * cos(x) else: return # raise NotImplementedError("Double angle for sin with num > 4 is not implemented") diff --git a/tests/test_khan_academy_integrals.py b/tests/test_khan_academy_integrals.py index 6406dc8..c385a6e 100644 --- a/tests/test_khan_academy_integrals.py +++ b/tests/test_khan_academy_integrals.py @@ -217,3 +217,17 @@ def test_trigonometric_substitution_tan_sub(): ans = integrate(expr) expected_ans = x / (8 * sqrt(x**2 / 4 + 1)) assert_eq_plusc(expected_ans, ans) + + +def test_trig_sub_sec_sub(): + # this one is not from KH + # perhaps I should stop sorting tests using KH or not but through transform or smtn. + expr = 3 * (25 - x**2) ** Fraction(5 / 2) + ans = integrate(expr) + expected_ans = ( + 5 * x**5 * sqrt(-(x**2) / 25 + 1) / 2 + + 103125 * x * sqrt(-(x**2) / 25 + 1) / 16 + - 1625 * x**3 * sqrt(-(x**2) / 25 + 1) / 8 + + 234375 * asin(x / 5) / 16 + ) + assert_eq_plusc(ans, expected_ans)