V1 untested
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,6 +2,7 @@
|
|||||||
#Steps that are generated
|
#Steps that are generated
|
||||||
|
|
||||||
from sympy import *
|
from sympy import *
|
||||||
|
import re
|
||||||
from sympy.parsing.sympy_parser import (
|
from sympy.parsing.sympy_parser import (
|
||||||
parse_expr,
|
parse_expr,
|
||||||
standard_transformations,
|
standard_transformations,
|
||||||
@@ -94,8 +95,8 @@ def multiply_both_sides(equation, value):
|
|||||||
left_expr = parse_expr(left, transformations=transformations, evaluate=False)
|
left_expr = parse_expr(left, transformations=transformations, evaluate=False)
|
||||||
right_expr = parse_expr(right, transformations=transformations, evaluate=False)
|
right_expr = parse_expr(right, transformations=transformations, evaluate=False)
|
||||||
|
|
||||||
new_left_expr = left_expr * value
|
new_left_expr = cancel(left_expr * value)
|
||||||
new_right_expr = right_expr * value
|
new_right_expr = Mul(right_expr, value, evaluate=False)
|
||||||
step["after"] = f"{sstr(new_left_expr)} = {sstr(new_right_expr)}"
|
step["after"] = f"{sstr(new_left_expr)} = {sstr(new_right_expr)}"
|
||||||
|
|
||||||
step["step"] = f"Multiply both sides by {sstr(value)}"
|
step["step"] = f"Multiply both sides by {sstr(value)}"
|
||||||
@@ -253,8 +254,12 @@ def trinomial_by_grouping(equation, inner):
|
|||||||
new_left_expr = left_expr
|
new_left_expr = left_expr
|
||||||
|
|
||||||
steps[-1]["after"] = f"{sstr(new_left_expr)} = {sstr(right_expr)}"
|
steps[-1]["after"] = f"{sstr(new_left_expr)} = {sstr(right_expr)}"
|
||||||
steps[-1]["step"] = f"Seperate the x coeficients equal to the factors of {sstr(ac)} that {action} {sstr(Abs(b))}"
|
steps[-1]["step"] = (
|
||||||
steps[-1]["rule"] = "Split Coeficients"
|
f"Seperate the x coefficient to equal the factors of the first times last coefficients"
|
||||||
|
f"({sstr(Abs(a))} x {sstr(Abs(c))} = {sstr(ac)}) that {action} {sstr(Abs(b))}"
|
||||||
|
f"({sstr(factor1)},{sstr(factor2)})"
|
||||||
|
)
|
||||||
|
steps[-1]["rule"] = "Split Coefficients"
|
||||||
|
|
||||||
## Factor Out X on left term
|
## Factor Out X on left term
|
||||||
steps.append({})
|
steps.append({})
|
||||||
@@ -262,6 +267,7 @@ def trinomial_by_grouping(equation, inner):
|
|||||||
|
|
||||||
terms = left_expr.as_ordered_terms()
|
terms = left_expr.as_ordered_terms()
|
||||||
t1, t2, t3, t4 = terms[0], terms[1], terms[2], terms[3]
|
t1, t2, t3, t4 = terms[0], terms[1], terms[2], terms[3]
|
||||||
|
div = gcd(t3, t4)
|
||||||
factored_part1 = factor(t1 + t2)
|
factored_part1 = factor(t1 + t2)
|
||||||
factored_part2 = factor(t3 + t4)
|
factored_part2 = factor(t3 + t4)
|
||||||
rest = sum(terms[2:])
|
rest = sum(terms[2:])
|
||||||
@@ -280,7 +286,7 @@ def trinomial_by_grouping(equation, inner):
|
|||||||
new_left_expr = Mul(n, left_expr, evaluate=False)
|
new_left_expr = Mul(n, left_expr, evaluate=False)
|
||||||
|
|
||||||
steps[-1]["after"] = f"{sstr(new_left_expr)} = {sstr(right_expr)}"
|
steps[-1]["after"] = f"{sstr(new_left_expr)} = {sstr(right_expr)}"
|
||||||
steps[-1]["step"] = f"Factor out the GCD from the right two terms of the inner polynomial"
|
steps[-1]["step"] = f"Factor out the GCD({sstr(div)}) from the right two terms of the inner polynomial"
|
||||||
steps[-1]["rule"] = "Reverse Distributive Property"
|
steps[-1]["rule"] = "Reverse Distributive Property"
|
||||||
|
|
||||||
## Add Like Terms
|
## Add Like Terms
|
||||||
@@ -311,9 +317,30 @@ def solve_roots(equation):
|
|||||||
x = symbols('x')
|
x = symbols('x')
|
||||||
|
|
||||||
left_expr = parse_expr(left, transformations=transformations, evaluate=False)
|
left_expr = parse_expr(left, transformations=transformations, evaluate=False)
|
||||||
factors = left_expr.as_ordered_factors()
|
|
||||||
x_factors = [f for f in factors if f.has(x)]
|
|
||||||
|
|
||||||
|
## Get the roots
|
||||||
|
factors = left_expr.as_ordered_factors()
|
||||||
|
x_factors = []
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while i < len(factors):
|
||||||
|
f = factors[i]
|
||||||
|
|
||||||
|
if f.has(x):
|
||||||
|
# If it's already something like 2*x or (x+...)
|
||||||
|
x_factors.append(f)
|
||||||
|
i += 1
|
||||||
|
else:
|
||||||
|
# Check if next factor has x → combine them
|
||||||
|
if i + 1 < len(factors) and factors[i + 1].has(x) and not factors[i + 1].is_Add:
|
||||||
|
combined = Mul(f, factors[i + 1], evaluate=False)
|
||||||
|
x_factors.append(combined)
|
||||||
|
i += 2
|
||||||
|
else:
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
|
||||||
|
## Iterate through the roots
|
||||||
solutions = ""
|
solutions = ""
|
||||||
for factor in x_factors:
|
for factor in x_factors:
|
||||||
clean_factor = clean(factor)
|
clean_factor = clean(factor)
|
||||||
@@ -399,7 +426,7 @@ def combine_like_terms(equation):
|
|||||||
step["rule"] = "Combine the like terms"
|
step["rule"] = "Combine the like terms"
|
||||||
return step
|
return step
|
||||||
|
|
||||||
def distribute_step(equation):
|
def distribute_left_step(equation):
|
||||||
steps = []
|
steps = []
|
||||||
|
|
||||||
current = equation
|
current = equation
|
||||||
@@ -416,12 +443,101 @@ def distribute_step(equation):
|
|||||||
if distributed != None:
|
if distributed != None:
|
||||||
steps.append({})
|
steps.append({})
|
||||||
steps[-1]["before"] = current
|
steps[-1]["before"] = current
|
||||||
steps[-1]["after"] = f"{sstr(safe_format(new_left_expr))} = {sstr(right_expr)}"
|
steps[-1]["after"] = f"{sstr(safe_format(new_left_expr))} = {sstr(safe_format(right_expr))}"
|
||||||
steps[-1]["step"] = f"Distribute out {sstr(distributed)}"
|
steps[-1]["step"] = f"Distribute out {sstr(distributed)}"
|
||||||
steps[-1]["rule"] = "Distributive Law of Multiplication"
|
steps[-1]["rule"] = "Distributive Law of Multiplication"
|
||||||
|
|
||||||
return steps
|
return steps
|
||||||
|
|
||||||
|
def distribute_right_step(equation):
|
||||||
|
steps = []
|
||||||
|
|
||||||
|
current = equation
|
||||||
|
left, right = current.split("=")
|
||||||
|
left = left.replace("-(", "-1*(")
|
||||||
|
x = symbols('x')
|
||||||
|
|
||||||
|
left_expr = parse_expr(left, transformations=transformations, evaluate=False)
|
||||||
|
right_expr = parse_expr(right, transformations=transformations, evaluate=False)
|
||||||
|
|
||||||
|
#print(f"calling distribute_once with expression: {sstr(left_expr)}")
|
||||||
|
new_right_expr, distributed = distribute_once(right_expr)
|
||||||
|
|
||||||
|
if distributed != None:
|
||||||
|
steps.append({})
|
||||||
|
steps[-1]["before"] = current
|
||||||
|
steps[-1]["after"] = f"{sstr(safe_format(left_expr))} = {sstr(safe_format(new_right_expr))}"
|
||||||
|
steps[-1]["step"] = f"Distribute out {sstr(distributed)}"
|
||||||
|
steps[-1]["rule"] = "Distributive Law of Multiplication"
|
||||||
|
|
||||||
|
return steps
|
||||||
|
|
||||||
|
def check_roots(equation, roots):
|
||||||
|
steps = []
|
||||||
|
|
||||||
|
valid_roots = ""
|
||||||
|
str_values = [r.split("=")[1].strip() for r in roots.split(",")]
|
||||||
|
values = [sympify(value) for value in str_values]
|
||||||
|
current = equation
|
||||||
|
left, right = current.split("=")
|
||||||
|
x = symbols('x')
|
||||||
|
left_expr = parse_expr(left, transformations=transformations, evaluate=False)
|
||||||
|
right_expr = parse_expr(right, transformations=transformations, evaluate=False)
|
||||||
|
|
||||||
|
# Check the roots
|
||||||
|
for value in values:
|
||||||
|
## Substitution
|
||||||
|
left_subbed = substitute_var(left, 'x', f"{value}")
|
||||||
|
right_subbed = substitute_var(right, 'x', f"{value}")
|
||||||
|
left_subbed_exp = parse_expr(left_subbed)
|
||||||
|
right_subbed_exp = parse_expr(right_subbed)
|
||||||
|
steps.append({})
|
||||||
|
steps[-1]["before"] = equation
|
||||||
|
steps[-1]["after"] = f"{left_subbed} = {right_subbed}"
|
||||||
|
steps[-1]["step"] = f"Substitute x with {value}"
|
||||||
|
steps[-1]["rule"] = "Substitution"
|
||||||
|
## Check
|
||||||
|
l_result = simplify(left_subbed_exp)
|
||||||
|
r_result = simplify(right_subbed_exp)
|
||||||
|
if l_result in (zoo, oo, -oo, nan) or r_result in (zoo, oo, -oo, nan):
|
||||||
|
steps.append({})
|
||||||
|
steps[-1]["before"] = steps[-2]["after"]
|
||||||
|
steps[-1]["after"] = f"Undefined"
|
||||||
|
steps[-1]["step"] = f"Found Extraneous Root, {value} is incorrect"
|
||||||
|
steps[-1]["rule"] = "Extraneous Root"
|
||||||
|
continue
|
||||||
|
|
||||||
|
if l_result != r_result:
|
||||||
|
steps.append({})
|
||||||
|
steps[-1]["before"] = steps[-2]["after"]
|
||||||
|
steps[-1]["after"] = f"{sstr(l_result)} ≠ {sstr(r_result)}"
|
||||||
|
steps[-1]["step"] = f"Found Extraneous Root, {value} is incorrect"
|
||||||
|
steps[-1]["rule"] = "Extraneous Root"
|
||||||
|
continue
|
||||||
|
|
||||||
|
else:
|
||||||
|
steps.append({})
|
||||||
|
steps[-1]["before"] = steps[-2]["after"]
|
||||||
|
steps[-1]["after"] = f"{sstr(l_result)} = {sstr(r_result)}"
|
||||||
|
steps[-1]["step"] = f"{value} is correct"
|
||||||
|
steps[-1]["rule"] = "Found Root"
|
||||||
|
if len(valid_roots) > 0:
|
||||||
|
valid_roots += ","
|
||||||
|
valid_roots += f"x = {value}"
|
||||||
|
|
||||||
|
|
||||||
|
steps.append({})
|
||||||
|
steps[-1]["before"] = equation
|
||||||
|
steps[-1]["after"] = valid_roots
|
||||||
|
steps[-1]["step"] = f"Found Valid Roots"
|
||||||
|
steps[-1]["rule"] = "Found Solution"
|
||||||
|
|
||||||
|
return steps
|
||||||
|
|
||||||
|
def substitute_var(expr, var, value):
|
||||||
|
pattern = rf'\b{re.escape(var)}\b'
|
||||||
|
return re.sub(pattern, f'({value})', expr)
|
||||||
|
|
||||||
def build_ordered_add(args):
|
def build_ordered_add(args):
|
||||||
flat_args = []
|
flat_args = []
|
||||||
|
|
||||||
@@ -521,12 +637,35 @@ def clean(expr):
|
|||||||
def safe_format(expr):
|
def safe_format(expr):
|
||||||
if expr.is_Mul:
|
if expr.is_Mul:
|
||||||
args = []
|
args = []
|
||||||
|
sign = 1
|
||||||
|
|
||||||
for a in expr.args:
|
for a in expr.args:
|
||||||
a = safe_format(a)
|
a = safe_format(a)
|
||||||
|
|
||||||
if a == 1:
|
if a == 1:
|
||||||
continue
|
continue
|
||||||
args.append(a)
|
elif a == -1:
|
||||||
return Mul(*args, evaluate=False)
|
sign *= -1
|
||||||
|
else:
|
||||||
|
args.append(a)
|
||||||
|
|
||||||
|
if not args:
|
||||||
|
return -1 if sign == -1 else 1
|
||||||
|
|
||||||
|
# if everything is numeric → evaluate fully
|
||||||
|
if all(a.is_Number for a in args):
|
||||||
|
val = Mul(*args)
|
||||||
|
return -val if sign == -1 else val
|
||||||
|
|
||||||
|
if len(args) == 1:
|
||||||
|
result = args[0]
|
||||||
|
else:
|
||||||
|
result = Mul(*args, evaluate=False)
|
||||||
|
|
||||||
|
if sign == -1:
|
||||||
|
return Mul(-1, result, evaluate=False)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
if expr.is_Add:
|
if expr.is_Add:
|
||||||
return expr.func(*[safe_format(a) for a in expr.args], evaluate=False)
|
return expr.func(*[safe_format(a) for a in expr.args], evaluate=False)
|
||||||
|
|||||||
19
main.py
19
main.py
@@ -7,7 +7,7 @@ from sympy import init_printing
|
|||||||
|
|
||||||
#define the entry point to the programs
|
#define the entry point to the programs
|
||||||
def main():
|
def main():
|
||||||
init_printing(order='none')
|
init_printing(order='lex')
|
||||||
problem = generate_problem()
|
problem = generate_problem()
|
||||||
steps = generate_steps(problem);
|
steps = generate_steps(problem);
|
||||||
|
|
||||||
@@ -15,7 +15,22 @@ def main():
|
|||||||
print(problem)
|
print(problem)
|
||||||
|
|
||||||
print("Steps:")
|
print("Steps:")
|
||||||
print(steps)
|
pretty_print_steps(steps)
|
||||||
|
|
||||||
|
def pretty_print_steps(steps):
|
||||||
|
print("\n" + "=" * 50)
|
||||||
|
|
||||||
|
for i, step in enumerate(steps, start=1):
|
||||||
|
print(f"\nStep {i}")
|
||||||
|
print("-" * 50)
|
||||||
|
print(f"Before: {step.get('before', '')}")
|
||||||
|
print(f"After: {step.get('after', '')}")
|
||||||
|
|
||||||
|
for key in step:
|
||||||
|
if key not in ("before", "after"):
|
||||||
|
print(f"{key.capitalize()}: {step[key]}")
|
||||||
|
|
||||||
|
print("\n" + "=" * 50)
|
||||||
|
|
||||||
#Starts the program
|
#Starts the program
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -120,7 +120,6 @@ def generate_quadratic ():
|
|||||||
|
|
||||||
x = symbols('x')
|
x = symbols('x')
|
||||||
expr = n *(s * x - r1) * (x - r2)
|
expr = n *(s * x - r1) * (x - r2)
|
||||||
print(f"n:{n}, s:{s}")
|
|
||||||
expr = expand(expr)
|
expr = expand(expr)
|
||||||
root1 = Rational(r1, s)
|
root1 = Rational(r1, s)
|
||||||
root2 = Integer(r2)
|
root2 = Integer(r2)
|
||||||
@@ -235,9 +234,12 @@ def generate_binomial ():
|
|||||||
@register_problem_generator("tricky")
|
@register_problem_generator("tricky")
|
||||||
def generate_tricky ():
|
def generate_tricky ():
|
||||||
#(x² - x - a) / (x + b) = c
|
#(x² - x - a) / (x + b) = c
|
||||||
|
r1 = 0
|
||||||
|
r2 = 0
|
||||||
|
while r1 == r2 or r1 == 0 and r2 == 0:
|
||||||
|
r1 = random.choice(range(-10, 13))
|
||||||
|
r2 = random.choice(range(-10, 13))
|
||||||
n = random.choice([i for i in range(-5, 6) if i != 0])
|
n = random.choice([i for i in range(-5, 6) if i != 0])
|
||||||
r1 = random.choice([i for i in range(-10, 16)])
|
|
||||||
r2 = random.choice([i for i in range(-10, 16) if i != r1])
|
|
||||||
|
|
||||||
x = symbols('x')
|
x = symbols('x')
|
||||||
expr = (x - r1) * (x - r2)
|
expr = (x - r1) * (x - r2)
|
||||||
@@ -263,6 +265,6 @@ def generate_problem():
|
|||||||
|
|
||||||
problem_type = random.choices(types, weights=weights)[0]
|
problem_type = random.choices(types, weights=weights)[0]
|
||||||
template = TEMPLATES[problem_type]
|
template = TEMPLATES[problem_type]
|
||||||
return generate_binomial()
|
return generate_tricky()
|
||||||
#return template()
|
#return template()
|
||||||
|
|
||||||
@@ -242,6 +242,10 @@ def generate_quadratic_steps (problem):
|
|||||||
|
|
||||||
##Solve the Roots
|
##Solve the Roots
|
||||||
steps.extend(algebraic_steps.solve_roots(current))
|
steps.extend(algebraic_steps.solve_roots(current))
|
||||||
|
current = steps[-1]["after"]
|
||||||
|
|
||||||
|
# Check for incorrect answers
|
||||||
|
steps.extend(algebraic_steps.check_roots(problem["problem"], current))
|
||||||
|
|
||||||
return steps
|
return steps
|
||||||
|
|
||||||
@@ -270,7 +274,10 @@ def generate_difference_squares_steps (problem):
|
|||||||
|
|
||||||
## Step 2
|
## Step 2
|
||||||
steps.append(algebraic_steps.square_root_both_sides(current))
|
steps.append(algebraic_steps.square_root_both_sides(current))
|
||||||
|
current = steps[-1]["after"]
|
||||||
|
|
||||||
|
# Check for incorrect answers
|
||||||
|
steps.extend(algebraic_steps.check_roots(problem["problem"], current))
|
||||||
|
|
||||||
return steps
|
return steps
|
||||||
|
|
||||||
@@ -310,6 +317,11 @@ def generate_radical_steps (problem):
|
|||||||
left_expr = parse_expr(left, transformations=transformations)
|
left_expr = parse_expr(left, transformations=transformations)
|
||||||
right_expr = parse_expr(right)
|
right_expr = parse_expr(right)
|
||||||
steps[-1]["after"] = f"{sstr(left_expr)} = {sstr(right_expr)}"
|
steps[-1]["after"] = f"{sstr(left_expr)} = {sstr(right_expr)}"
|
||||||
|
|
||||||
|
current = steps[-1]["after"]
|
||||||
|
|
||||||
|
# Check for incorrect answers
|
||||||
|
steps.extend(algebraic_steps.check_roots(problem["problem"], current))
|
||||||
|
|
||||||
return steps
|
return steps
|
||||||
|
|
||||||
@@ -353,12 +365,14 @@ def generate_binomial_steps (problem):
|
|||||||
current = problem["problem"]
|
current = problem["problem"]
|
||||||
|
|
||||||
## Distribute Terms
|
## Distribute Terms
|
||||||
|
init_printing(order='none')
|
||||||
last_len = -1
|
last_len = -1
|
||||||
while last_len != len(steps):
|
while last_len != len(steps):
|
||||||
last_len = len(steps)
|
last_len = len(steps)
|
||||||
steps.extend(algebraic_steps.distribute_step(current))
|
steps.extend(algebraic_steps.distribute_left_step(current))
|
||||||
if len(steps):
|
if len(steps):
|
||||||
current = steps[-1]["after"]
|
current = steps[-1]["after"]
|
||||||
|
init_printing(order='lex')
|
||||||
|
|
||||||
## Combine Like Terms
|
## Combine Like Terms
|
||||||
steps.append(algebraic_steps.combine_like_terms(current))
|
steps.append(algebraic_steps.combine_like_terms(current))
|
||||||
@@ -376,6 +390,9 @@ def generate_binomial_steps (problem):
|
|||||||
current = steps[-1]["after"]
|
current = steps[-1]["after"]
|
||||||
left, right = current.split("=")
|
left, right = current.split("=")
|
||||||
left_expr = parse_expr(left, transformations=transformations)
|
left_expr = parse_expr(left, transformations=transformations)
|
||||||
|
right_expr = parse_expr(right)
|
||||||
|
steps[-1]["after"] = f"{sstr(left_expr)} = {sstr(right_expr)}"
|
||||||
|
current = steps[-1]["after"]
|
||||||
|
|
||||||
## Divide by coefficient
|
## Divide by coefficient
|
||||||
a = left_expr.coeff(x)
|
a = left_expr.coeff(x)
|
||||||
@@ -390,6 +407,38 @@ def generate_binomial_steps (problem):
|
|||||||
def generate_tricky_steps (problem):
|
def generate_tricky_steps (problem):
|
||||||
#(x² - x - a) / (x + b) = c
|
#(x² - x - a) / (x + b) = c
|
||||||
steps = []
|
steps = []
|
||||||
|
x = symbols('x')
|
||||||
|
current = problem["problem"]
|
||||||
|
|
||||||
|
# Multiply Denom
|
||||||
|
left, right = current.split("=")
|
||||||
|
left_expr = parse_expr(left, transformations=transformations)
|
||||||
|
right_expr = parse_expr(right)
|
||||||
|
num, den = left_expr.as_numer_denom()
|
||||||
|
steps.append(algebraic_steps.multiply_both_sides(current, den))
|
||||||
|
current = steps[-1]["after"]
|
||||||
|
|
||||||
|
# Distribute out right side
|
||||||
|
init_printing(order='none')
|
||||||
|
steps.extend(algebraic_steps.distribute_right_step(current))
|
||||||
|
current = steps[-1]["after"]
|
||||||
|
init_printing(order='lex')
|
||||||
|
|
||||||
|
# Move Everything to Left
|
||||||
|
steps.append(algebraic_steps.move_all_to_one_side(current))
|
||||||
|
current = steps[-1]["after"]
|
||||||
|
|
||||||
|
# Combine Like Terms
|
||||||
|
steps.append(algebraic_steps.combine_like_terms(current))
|
||||||
|
current = steps[-1]["after"]
|
||||||
|
|
||||||
|
# Solve Quadratic
|
||||||
|
print(f"calling generate_quadratic_steps with: {current}")
|
||||||
|
steps.extend(generate_quadratic_steps({"problem" : current}))
|
||||||
|
current = steps[-1]["after"]
|
||||||
|
|
||||||
|
# Check for incorrect answers
|
||||||
|
steps.extend(algebraic_steps.check_roots(problem["problem"], current))
|
||||||
|
|
||||||
return steps
|
return steps
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user