
# Utility code
import sympy as sym
from sympy import latex, LambertW, CRootOf, N
# Function to check if an expression leads to a transcendental equation
def try_to_solve_internal(expression, variable):
if isinstance(expression, list):
expression = expression[0]
timeout = True
else:
timeout = False
equation = sym.Eq(expression, 0)
if timeout:
return (equation, None, "No, SymPy Limitation (Timeout)")
# Try to solve the equation
try:
solutions = sym.solve(equation, variable)
if solutions:
if any(sol.has(LambertW) for sol in solutions):
return (equation, solutions, "Yes, with LambertW")
elif any(sol.has(CRootOf) for sol in solutions):
return (equation, None, "No, SymPy uses CRootOf")
else:
return (equation, solutions, "Yes, Elementary")
else:
return (equation, solutions, "No, SymPy returns 0 Solutions")
except Exception as e:
return (equation, None, f"No, SymPy Limitation ({e.__class__.__name__})")
def try_to_solve(expression, variable):
equation, solutions, category = try_to_solve_internal(
expression, variable
)
# if solutions is None, try numeric solving
if solutions is None:
try:
numeric = sym.nsolve(equation, 1)
except Exception as _:
try:
numeric = sym.nsolve(equation, 0)
except Exception as e:
numeric = e.__class__.__name__
elif len(solutions) == 0:
numeric = sym.nsolve(equation, 0)
else:
numeric = N(solutions[0])
return equation, solutions, category, numeric
def generate_markdown_table_row(equation, solutions, category, numeric):
# Convert equation and solution to LaTeX format
equation_md = f"${latex(equation)}$"
if solutions is None:
solution_md = ""
elif len(solutions) == 0:
solution_md = ""
elif len(solutions) == 1:
solution = solutions[0]
solution_md = f"${latex(solution)}$"
elif len(solutions) == 2:
# Show both solutions if there are exactly 2
solutions_latex = ", ".join([latex(sol) for sol in solutions])
solution_md = f"$\\{{ {solutions_latex} \\}}$" # Format as a set
else:
op_count = solutions[0].count_ops() + 1
solution_md = r"\{{ " + latex(solutions[0]) + ", "
for sol in solutions[1:]:
op_count += sol.count_ops() + 1
if op_count >= 30:
break
solution_md += f"{latex(sol)}, "
solution_md += r"\dots \}}"
solution_md = f"${solution_md}$"
if numeric is None:
numeric_md = "*n/a*"
elif isinstance(numeric, str):
numeric_md = numeric
elif numeric.is_real:
numeric_md = f"{numeric:.4f}" # Format regular numbers
else:
real_part, imag_part = numeric.as_real_imag()
numeric_md = f"{real_part:.4f} + {imag_part:.4f}i" # Format complex numbers
# Create the markdown table row
markdown_row = f"| {equation_md} | {category} | {solution_md} | {numeric_md} |\n"
return markdown_row
def generate_markdown_table(tuple_list):
full_table = "| Equation | SymPy Closed-Form (CF) Solution? | CF Solution(s) | A Numeric |\n|----------|----------|----------|-------|\n"
for equation, solution, category, numeric in tuple_list:
table_row = generate_markdown_table_row(equation, solution, category, numeric)
full_table += table_row
return full_table
def split_on_predicate(lst, predicate):
result = []
current_group = []
for item in lst:
if predicate(item):
# If we already have a group, add it to the result
if current_group:
result.append(current_group)
# Start a new group with the item that matched the predicate
current_group = [item]
else:
# If the item does not match the predicate, add it to the current group
current_group.append(item)
# Append the last group at the end
if current_group:
result.append(current_group)
return result
from sympy import sin, cos, exp, log, sqrt, pi, Rational
from IPython.display import Markdown, display
# Define the symbolic variable x and numerator 1
x = sym.symbols("x")
one = sym.Integer(1)
# Transformed combinations into single expressions (add 1 to all multiply cases)
expressions = [
"Kepler's Equation",
x - 0.967 * sin(x) - Rational(20,76) * 2 * pi,
"Polynomials",
x**2 + (-x - 1),
2 * x**3 + x + 1,
x**2 * x**3 + 1,
x**5 + (-x - 1),
"Exp, Log and *x*",
x * exp(x),
x * exp(x) + Rational(1,10),
x + exp(x),
x * log(x) + 1,
x + log(x),
x**2 + log(x),
"Exp and Log",
log(x) * exp(x) + 1,
log(x) + exp(x),
"Trig and Trig Same Frequency",
cos(x) + sin(x),
cos(x) * sin(x) + 1,
sin(x) + sin(x + 1),
sin(x) * sin(x + 1) + 1,
"Trig and Trig, Commensurate Frequencies",
sin(3 * x) + sin(x),
sin(3 * x) + (sin(x) + 1),
sin(3 * x) * sin(x) + 1,
[sin(3 * x) + sin(x + 1)],
[sin(3 * x) * sin(x + 1) + 1],
"Trig and Trig, Non-Commensurate Frequencies",
sin(sqrt(3) * x) + sin(x),
sin(sqrt(3) * x) + (sin(x) + 1),
sin(sqrt(3) * x) + sin(x + 1),
sin(sqrt(3) * x) * sin(x) + 1,
sin(sqrt(3) * x) * sin(x + 1) + 1,
"Trigonometric and *x*, Exp, Log",
x + sin(x),
x * sin(x) + 1,
sin(x) + log(x),
exp(x) + sin(x),
x**3 + cos(x),
]
groups = split_on_predicate(expressions, lambda x: isinstance(x, str))
for group in groups:
group_title = f"### {group[0]}"
display(Markdown(group_title))
results = [try_to_solve(expression, variable=x) for expression in group[1:]]
table = generate_markdown_table(results)
display(Markdown(table))
<IPython.core.display.Markdown object><IPython.core.display.Markdown object><IPython.core.display.Markdown object><IPython.core.display.Markdown object><IPython.core.display.Markdown object><IPython.core.display.Markdown object><IPython.core.display.Markdown object><IPython.core.display.Markdown object><IPython.core.display.Markdown object><IPython.core.display.Markdown object><IPython.core.display.Markdown object><IPython.core.display.Markdown object><IPython.core.display.Markdown object><IPython.core.display.Markdown object><IPython.core.display.Markdown object><IPython.core.display.Markdown object>