Skip to content

Instantly share code, notes, and snippets.

@vedantk
Created May 27, 2011 14:51
Show Gist options
  • Save vedantk/995400 to your computer and use it in GitHub Desktop.
Save vedantk/995400 to your computer and use it in GitHub Desktop.
latest expr
import re
import ast
import random
import math
import operator
from bigfloat import *
h = 10 ** -10
N = int(math.sqrt(1 / h))
idhash = lambda s: hash(s) % (10 ** 3)
static = lambda k: lambda x: BigFloat(k)
unary = {
"ln": log,
"exp": exp,
"abs": abs,
"sqrt": sqrt,
"sin": sin,
"cos": cos,
"tan": tan,
"arcsin": asin,
"arccos": acos,
"arctan": atan,
"csc": csc,
"sec": sec,
"cot": cot,
}
binary = {
ast.Add: ('+', add),
ast.Sub: ('-', sub),
ast.Mult: ('*', mul),
ast.Div: ('/', div),
ast.Pow: ('^', pow),
}
special = {
"e": exp(1),
"pi": const_pi(),
ast.USub: lambda x: -x,
}
for name in ["u", "n", "a", "b", "du"]:
special[name] = idhash(name)
special_funcs = set(["f", "g", "f_prime", "g_prime"])
for name in special_funcs:
special[name] = lambda x: x + idhash(name)
def VarFunc_resolver(match):
vf = match.group(1)
if vf in unary or vf in special_funcs:
return vf + '('
return vf + '*('
rewrite = (
[r'\[', r'('],
[r'\]', r')'],
[r'\^', r'**'],
[r'\'', r'_prime'],
# "Var(" but not "Func("
[re.compile(r'([a-z_\']+)\('), VarFunc_resolver],
# "NumFunc|Var" or "Num(" or ")(" or ")Func|Var"
[re.compile(r'(\)|\d+)([a-z_\'\(])'), r'\g<1>*\g<2>'],
)
def parse(s):
'Create a nested-lambda expression from a string.'
s = s.replace(' ', '')
for rule, pattern in rewrite:
s = re.sub(rule, pattern, s)
print(":: [Parser] " + s)
try:
return build_expr(ast.parse(s))
except:
return static(0)
ast_hints = ast.Name, ast.Num, ast.BinOp, ast.Call, ast.UnaryOp
def build_expr(node):
'Search for a lambdify-able AST node.'
child = ast.iter_child_nodes(node)
for sub in child:
if type(sub) in ast_hints:
return lambdify(sub)
return build_expr(sub)
print(ast.dump(node))
raise Exception("Error: couldn't parse the AST.")
def lambdify(node):
'Convert AST nodes to their lambda equivalents.'
if isinstance(node, ast.Name):
return lambda x: special.get(node.id, x)
elif isinstance(node, ast.Num):
return static(node.n)
elif isinstance(node, ast.BinOp):
_, fn = binary[type(node.op)]
lfx, rfx = map(lambdify, (node.left, node.right))
return lambda x: fn(lfx(x), rfx(x))
elif isinstance(node, ast.Call):
name = node.func.id
func = unary.get(name, special.get(name))
fx = lambdify(node.args[0])
elif isinstance(node, ast.UnaryOp):
func = special[type(node.op)]
fx = lambdify(node.operand)
return lambda x: func(fx(x))
pick_op = lambda table: random.choice(list(table.keys()))
def make_unary(child):
op = pick_op(unary)
fx = lambda x: unary[op](child[1](x))
return (op, child[0]), fx
def make_binary(lhs, rhs):
op = pick_op(binary)
name, fn = binary[op]
fx = lambda x: fn(lhs[1](x), rhs[1](x))
return (name, lhs[0], rhs[0]), fx
def make_var():
k, expt = random.randint(2, 10), random.randint(1, 10)
fmt = "{0}x{1}".format(k, "^{0}".format(expt) if expt > 1 else "")
return fmt, lambda x: k * (x ** expt)
def func_gen(size):
'Generate a function and its string representation given a size.'
prob = random.random()
if size == 0 or prob < 0.25:
return make_var()
elif prob < 0.65:
return make_unary(func_gen(size - 1))
else:
return make_binary(func_gen(size // 2), func_gen(size // 2))
def func_infix(elt):
if isinstance(elt, tuple):
if elt[0] in unary:
return elt[0] + '(' + func_infix(elt[1]) + ')'
else:
return ' '.join([
func_infix(elt[1]), elt[0], func_infix(elt[2])
])
return elt
def equal(lhs, rhs):
'Compare floats for equality.'
return abs(lhs - rhs) < 0.001
domain = [0, .5, 1, 2, 9, special['e'], specal['pi']]
domain.extend([-n for n in domain[1:]])
domain = [BigFloat(str(n)) for n in domain]
def check(lfx, rfx):
'Check if two functions are equivalent.'
score, denom = 0, len(domain)
for elt in domain:
try:
if equal(lfx(elt), rfx(elt)):
score += 1
except:
denom -= 1
print("Score = {0}/{1}...".format(score, denom))
return score / denom > .5 if denom > (len(domain) / 2) else False
def differentiate(fx, k):
return (fx(k + h) - fx(k)) / h
def derivative(fx):
return lambda x: differentiate(fx, x)
def integrate(fx, a, b):
traps = (fx(a + (b - a) * k / N) for k in range(1, N))
return (b - a) * (fx(a) / 2 + fx(b) / 2 + sum(traps)) / N
def integral(fx):
return lambda x: integrate(fx, 0, x)
questions = [
("f(x) = {0}. f'(x) = ?", "d/dx f(x)", derivative),
("f(x) = {0}. f''(x) = ?", "d^2/dx^2 f(x)", lambda fx: derivative(derivative(fx))),
("f(x) = {0}. F(x) = ? + C", "integral(f(x))", integral),
]
identities = [
# Pythagorean Identities
("sin(x)^2 + cos(x)^2 = ?", "1",
("cos(x)^2 = ?", "(1 + cos(2x)) / 2"),
("sin(x)^2 = ?", "(1 - cos(2x)) / 2"),
# Tan/Cot Identities
("sin(u)/cos(u) = ?", "tan(u)"),
("cos(u)/sin(u) = ?", "cot(u)"),
# Reciprocal Identities
("1/sin(u) = ?", "csc(u)"),
("1/cos(u) = ?", "sec(u)"),
("1/tan(u) = ?", "cot(u)"),
("1/csc(u) = ?", "sin(u)"),
("1/sec(u) = ?", "cos(u)"),
("1/cot(u) = ?", "tan(u)"),
# Even/Odd Formulas
("sin(-u) = ?", "-sin(u)"),
("cos(-u) = ?", "cos(u)"),
("tan(-u) = ?", "-tan(u)"),
# Double Angle Formulas
("sin(2u) = ?", "2sin(u)cos(u)"),
("(cos(u)^2) - (sin(u)^2) = ?", "cos(2u)"),
("2(cos(u)^2) - 1 = ?", "cos(2u)"),
("1 - 2(sin(u)^2) = ?", "cos(2u)"),
("tan(2u) = ?", "2tan(u)/(1-(tan(u)^2))"),
# Angle Conversion
("pi radians = ? degrees", "180",
("180 degrees = ? radians", "pi"),
# Half Angle Formulas
("(1/2)(1 - cos(2u)) = ?", "sin(u)^2"),
("(1/2)(1 + cos(2u)) = ?", "cos(u)^2"),
("(1 - cos(2u))/(1 + cos(2u)) = ?", "tan(u)^2"),
# Sum and Difference Formulas
("sin(a + b) = ?", "sin(a)cos(b) + cos(a)sin(b)"),
("cos(a + b) = ?", "cos(a)cos(b) - sin(a)sin(b)"),
("tan(a + b) = ?", "(tan(a) + tan(b))/(1 - tan(a)tan(b))"),
# Product to Sum Formulas
("sin(a)sin(b) = ?", "(1/2)(cos(a - b) - cos(a + b))"),
("cos(a)cos(b) = ?", "(1/2)(cos(a - b) + cos(a + b))"),
("sin(a)cos(b) = ?", "(1/2)(sin(a + b) + sin(a - b))"),
("cos(a)sin(b) = ?", "(1/2)(sin(a + b) - sin(a - b))"),
# Sum to Product Formulas
("sin(a) + sin(b) = ?", "2sin((a + b)/2)cos((a - b)/2)"),
("sin(a) - sin(b) = ?", "2cos((a + b)/2)sin((a - b)/2)"),
("cos(a) + cos(b) = ?", "2cos((a + b)/2)cos((a - b)/2)"),
("cos(a) - cos(b) = ?", "-2sin((a + b)/2)sin((a - b)/2)"),
# Cofunction Formulas
("sin((pi/2) - a) = ?", "cos(a)"),
("cos((pi/2) - a) = ?", "sin(a)"),
("tan((pi/2) - a) = ?", "cot(a)"),
("csc((pi/2) - a) = ?", "sec(a)"),
("sec((pi/2) - a) = ?", "csc(a)"),
("cot((pi/2) - a) = ?", "tan(a)"),
# Basic Differentiation
("d/dx a = ?", "0",
("d/dx x = ?", "1",
("d/dx a*x = ?", "a"),
("d/dx a[f(x)] = ?", "a[f'(x)]"),
("d/dx [f(x) + g(x)] = ?", "f'(x) + g'(x)"),
("d/dx f(x)g(x) = ?", "f(x)g'(x) + f'(x)g(x)"),
("d/dx f(x)/g(x) = ?", "[f'(x)g(x) - f(x)g'(x)] / g(x)^2"),
("d/dx [1 / f(x)] = ?", "-f'(x) / f(x)^2"),
("d/dx f(g(x)) = ?", "f'(g(x))g'(x)"),
("d/dx u^n = ?", "n(u^(n-1))"),
("d/dx a^u = ?", "ln(a)(a^u) du"),
("d/dx log_a(u) = ?", "du/(u * ln(a))"),
("d/dx e^u = ?", "(e^u) du"),
("d/dx ln(u) = ?", "du/u"),
# Trig Differentiation
("d/dx sin(u) = ?", "cos(u) du"),
("d/dx cos(u) = ?", "-sin(u) du"),
("d/dx tan(u) = ?", "(sec(u)^2) du"),
("d/dx sec(u) = ?", "sec(u)tan(u) du"),
("d/dx csc(u) = ?", "-csc(u)cot(u) du"),
("d/dx cot(u) = ?", "-(csc(u)^2) du"),
# Inverse Trig Differentiation
("d/dx arcsin(u) = ?", "1/sqrt(1-(x^2))"),
("d/dx arccos(u) = ?", "-1/sqrt(1-(x^2))"),
("d/dx arctan(u) = ?", "1/(1+(x^2))"),
# Integration Formulas
# Volume, surface area, polar area, arclen, parametrics...
]
def generate():
'Generate a word problem and its solution.'
if random.random() < 0.33:
prob, soln = random.choice(identities)
return prob, soln, parse(soln)
else:
fstr, soln, fx = func_gen(random.randint(1, 3))
problem, solution = random.choice(questions)
prob = problem.format(func_infix(fstr))
return prob, soln, solution(fx)
def repl():
print("Calculus Blasters (REPL)")
while True:
prob, soln, sfx = generate()
print("\nCalculus> " + prob)
fx = parse(input("Blast> "))
if check(fx, sfx):
print("Correct!")
else:
action = input("Incorrect (expected {0}). Override (y/n)? ".format(soln))
print("OK. My bad." if 'y' in action.lower() else "Moving on then...")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment