algebrite
Version:
Computer Algebra System in Coffeescript
318 lines (239 loc) • 7.91 kB
text/coffeescript
###
Transform an expression using a pattern. The
pattern can come from the integrals table or
the user-defined patterns.
The expression and free variable are on the stack.
The argument s is a null terminated list of transform rules.
For example, see the itab (integrals table)
Internally, the following symbols are used:
F input expression
X free variable, i.e. F of X
A template expression
B result expression
C list of conditional expressions
Puts the final expression on top of stack
(whether it's transformed or not) and returns
true is successful, false if not.
###
# p1 and p2 are tmps
#define F p3
#define X p4
#define A p5
#define B p6
#define C p7
transform = (s, generalTransform) ->
transform_h = 0
save()
p1 = null
p4 = pop() # X i.e. free variable
p3 = pop() # F i.e. input expression
if DEBUG
console.log " !!!!!!!!! transform on: " + p3
saveMetaBindings()
set_binding(symbol(METAX), p4)
# put constants in F(X) on the stack
transform_h = tos
push_integer(1)
push(p3)
push(p4)
polyform(); # collect coefficients of x, x^2, etc.
push(p4)
bookmarkTosToPrintDecomps = tos - 2
decomp(generalTransform)
numberOfDecomps = tos - bookmarkTosToPrintDecomps
if DEBUG
console.log " " + numberOfDecomps + " decomposed elements ====== "
for i in [0...numberOfDecomps]
console.log " decomposition element " + i + ": " + stack[tos-1-i]
transformationSuccessful = false
if generalTransform
# "general tranform" mode is supposed to be more generic than
# "integrals" mode.
# In general transform mode we get only one transformation
# in s
# simple numbers can end up matching complicated templates,
# which we don't want.
# for example "1" ends up matching "inner(transpose(a_),a_)"
# since "1" is decomposed to "1" and replacing "a_" with "1"
# there is a match.
# Although this match is OK at some fundamental level, we want to
# avoid it because that's not what the spirit of this match
# is: "1" does not have any structural resemblance with
# "inner(transpose(a_),a_)". There are probably better ways
# to so this, for example we might notice that "inner" is an
# anchor since it "sits above" any meta variables, so we
# might want to mandate it to be matched at the top
# of the tree. For the time
# being let's just skip matching on simple numbers.
if !isnum(p3)
theTransform = s
if DEBUG then console.log "applying transform: " + theTransform
if DEBUG then console.log "scanning table entry " + theTransform
push theTransform
# replacements of meta variables. Note that we don't
# use scan_meta because the pattern is not a string
# that we have to parse, it's a tree already.
# replace a_ with METAA in the passed transformation
push symbol(SYMBOL_A_UNDERSCORE)
push symbol(METAA)
subst()
# replace b_ with METAB in the passed transformation
push symbol(SYMBOL_B_UNDERSCORE)
push symbol(METAB)
subst()
# replace x_ with METAX in the passed transformation
push symbol(SYMBOL_X_UNDERSCORE)
push symbol(METAX)
subst()
p1 = pop()
p5 = car(p1)
if DEBUG then console.log "template expression: " + p5
p6 = cadr(p1)
p7 = cddr(p1)
###
p5 = p1.tensor.elem[0]
p6 = p1.tensor.elem[1]
for i in [2..(p1.tensor.elem.length-1)]
push p1.tensor.elem[i]
list(p1.tensor.elem.length - 2)
p7 = pop()
###
if (f_equals_a(transform_h, generalTransform))
# successful transformation,
# transformed result is in p6
transformationSuccessful = true
else
# the match failed but perhaps we can match
# something lower down in the tree, so
# let's recurse the tree
if DEBUG then console.log "p3 at this point: " + p3
transformedTerms = []
if DEBUG then console.log "car(p3): " + car(p3)
restTerm = p3
if iscons(restTerm)
transformedTerms.push car(p3)
restTerm = cdr(p3)
while (iscons(restTerm))
secondTerm = car(restTerm)
restTerm = cdr(restTerm)
if DEBUG then console.log "tos before recursive transform: " + tos
push(secondTerm)
push_symbol(NIL)
if DEBUG then console.log "testing: " + secondTerm
#if (secondTerm+"") == "eig(A x,transpose(A x))()"
# debugger
if DEBUG then console.log "about to try to simplify other term: " + secondTerm
success = transform(s, generalTransform)
transformationSuccessful = transformationSuccessful or success
transformedTerms.push pop()
if DEBUG then console.log "tried to simplify other term: " + secondTerm + " ...successful?: " + success + " ...transformed: " + transformedTerms[transformedTerms.length-1]
# recreate the tree we were passed,
# but with all the terms being transformed
if transformedTerms.length != 0
for i in transformedTerms
push i
list(transformedTerms.length)
p6 = pop()
else # "integrals" mode
for eachTransformEntry in s
if DEBUG
console.log "scanning table entry " + eachTransformEntry
if (eachTransformEntry + "").indexOf("f(sqrt(a+b*x),2/3*1/b*sqrt((a+b*x)^3))") != -1
debugger
if eachTransformEntry
scan_meta(eachTransformEntry)
p1 = pop()
p5 = cadr(p1)
p6 = caddr(p1)
p7 = cdddr(p1)
###
p5 = p1.tensor.elem[0]
p6 = p1.tensor.elem[1]
for i in [2..(p1.tensor.elem.length-1)]
push p1.tensor.elem[i]
list(p1.tensor.elem.length - 2)
p7 = pop()
###
if (f_equals_a(transform_h, generalTransform))
# there is a successful transformation,
# transformed result is in p6
transformationSuccessful = true
break
moveTos transform_h
if transformationSuccessful
#console.log "transformation successful"
# a transformation was successful
push(p6)
Eval()
p1 = pop()
#console.log "...into: " + p1
transformationSuccessful = true
else
# transformations failed
if generalTransform
# result = original expression
p1 = p3
else
p1 = symbol(NIL)
restoreMetaBindings()
push(p1)
restore()
return transformationSuccessful
saveMetaBindings = ->
push(get_binding(symbol(METAA)))
push(get_binding(symbol(METAB)))
push(get_binding(symbol(METAX)))
restoreMetaBindings = ->
set_binding(symbol(METAX), pop())
set_binding(symbol(METAB), pop())
set_binding(symbol(METAA), pop())
# search for a METAA and METAB such that F = A
f_equals_a = (h, generalTransform) ->
fea_i = 0
fea_j = 0
for fea_i in [h...tos]
set_binding(symbol(METAA), stack[fea_i])
if DEBUG
console.log " binding METAA to " + get_binding(symbol(METAA))
for fea_j in [h...tos]
set_binding(symbol(METAB), stack[fea_j])
if DEBUG
console.log " binding METAB to " + get_binding(symbol(METAB))
# now test all the conditions (it's an and between them)
p1 = p7
while (iscons(p1))
push(car(p1))
Eval()
p2 = pop()
if (iszero(p2))
break
p1 = cdr(p1)
if (iscons(p1))
# conditions are not met,
# skip to the next binding of metas
continue
push(p3); # F = A?
if DEBUG
console.log "about to evaluate template expression: " + p5 +
" binding METAA to " + get_binding(symbol(METAA)) +
" and binding METAB to " + get_binding(symbol(METAB)) +
" and binding METAX to " + get_binding(symbol(METAX))
push(p5)
if generalTransform
originalexpanding = expanding
expanding = false
Eval()
if generalTransform
expanding = originalexpanding
if DEBUG
console.log " comparing " + stack[tos-1] + " to: " + stack[tos-2]
subtract()
p1 = pop()
if (iszero(p1))
if DEBUG
console.log "binding METAA to " + get_binding(symbol(METAA))
console.log "binding METAB to " + get_binding(symbol(METAB))
console.log "binding METAX to " + get_binding(symbol(METAX))
console.log "comparing " + p3 + " to: " + p5
return 1; # yes
return 0; # no