Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ venv*/
coverage.xml
sandbox/

# working benchmark directory
b/

# from pip install -e .
*.egg-info/
Expand Down
2 changes: 1 addition & 1 deletion benchmark/benchmark-profiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Binary file added benchmark/integration_log.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
93 changes: 93 additions & 0 deletions benchmark/integration_log.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
Integrand: time taken (s)

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



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)

4 changes: 4 additions & 0 deletions benchmark/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
38 changes: 37 additions & 1 deletion src/simpy/debug/logger.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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"))
Expand All @@ -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")


Expand Down
13 changes: 10 additions & 3 deletions src/simpy/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -326,9 +331,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())
Expand Down Expand Up @@ -2065,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")
Expand Down
Loading