UNPKG

algebrite

Version:

Computer Algebra System in Coffeescript

873 lines (722 loc) 20.4 kB
Eval_simplify = -> push(cadr(p1)) runUserDefinedSimplifications() Eval() simplify() runUserDefinedSimplifications = -> # ----------------------- # unfortunately for the time being user # specified simplifications are only # run in things which don't contain # integrals. # Doesn't work yet, could be because of # some clobbering as "transform" is called # recursively? if userSimplificationsInListForm.length != 0 and !Find(cadr(p1), symbol(INTEGRAL)) originalexpanding = expanding expanding = false if DEBUG then console.log("runUserDefinedSimplifications passed: " + stack[tos-1].toString()) Eval() if DEBUG then console.log("runUserDefinedSimplifications after eval no expanding: " + stack[tos-1].toString()) expanding = originalexpanding p1 = stack[tos-1] if DEBUG then console.log "patterns to be checked: " for eachSimplification in userSimplificationsInListForm if DEBUG then console.log "..." + eachSimplification atLeastOneSuccessInRouldOfRulesApplications = true numberOfRulesApplications = 0 while atLeastOneSuccessInRouldOfRulesApplications and numberOfRulesApplications < MAX_CONSECUTIVE_APPLICATIONS_OF_ALL_RULES atLeastOneSuccessInRouldOfRulesApplications = false numberOfRulesApplications++ for eachSimplification in userSimplificationsInListForm success = true eachConsecutiveRuleApplication = 0 while success and eachConsecutiveRuleApplication < MAX_CONSECUTIVE_APPLICATIONS_OF_SINGLE_RULE eachConsecutiveRuleApplication++ if DEBUG then console.log "simplify - tos: " + tos + " checking pattern: " + eachSimplification + " on: " + p1 push_symbol(NIL) success = transform(eachSimplification, true) if success atLeastOneSuccessInRouldOfRulesApplications = true p1 = stack[tos-1] if DEBUG then console.log "p1 at this stage of simplification: " + p1 if eachConsecutiveRuleApplication == MAX_CONSECUTIVE_APPLICATIONS_OF_SINGLE_RULE stop("maximum application of single transformation rule exceeded: " + eachSimplification) if numberOfRulesApplications == MAX_CONSECUTIVE_APPLICATIONS_OF_ALL_RULES stop("maximum application of all transformation rules exceeded ") if DEBUG console.log "METAX = " + get_binding(symbol(METAX)) console.log "METAA = " + get_binding(symbol(METAA)) console.log "METAB = " + get_binding(symbol(METAB)) # ------------------------ simplifyForCodeGeneration = -> save() runUserDefinedSimplifications() codeGen = true # in "codeGen" mode we completely # eval and simplify the function bodies # because we really want to resolve all # the variables indirections and apply # all the simplifications we can. simplify_main() codeGen = false restore() simplify = -> save() simplify_main() restore() simplify_main = -> p1 = pop() # when we do code generation, we proceed to # fully evaluate and simplify the body of # a function, so we resolve all variables # indirections and we simplify everything # we can given the current assignments. if codeGen and car(p1) == symbol(FUNCTION) fbody = cadr(p1) push fbody # let's simplify the body so we give it a # compact form eval() simplify() p3 = pop() # replace the evaled body args = caddr(p1); # p5 is B push_symbol(FUNCTION) push p3 push args list(3) p1 = pop() if (istensor(p1)) simplify_tensor() return if (Find(p1, symbol(FACTORIAL))) push(p1) simfac() p2 = pop() push(p1) rationalize() simfac() p3 = pop() if (count(p2) < count(p3)) p1 = p2 else p1 = p3 f10() f1() f2() f3() f4() f5() f9() simplify_polarRect() if do_simplify_nested_radicals # if there is some de-nesting then # re-run a simplification because # the shape of the expression might # have changed significantly. # e.g. simplify(14^(1/2) - (16 - 4*7^(1/2))^(1/2)) # needs some more semplification after the de-nesting. if simplify_nested_radicals() if DEBUG then console.log("de-nesting successful into: " + p1.toString()) push(p1) simplify() return simplify_rectToClock() push(p1) simplify_tensor = -> i = 0 p2 = alloc_tensor(p1.tensor.nelem) p2.tensor.ndim = p1.tensor.ndim for i in [0...p1.tensor.ndim] p2.tensor.dim[i] = p1.tensor.dim[i] for i in [0...p1.tensor.nelem] push(p1.tensor.elem[i]) simplify() p2.tensor.elem[i] = pop() check_tensor_dimensions p2 if (iszero(p2)) p2 = zero; # null tensor becomes scalar zero push(p2) # try rationalizing f1 = -> if (car(p1) != symbol(ADD)) return push(p1) rationalize() p2 = pop() if (count(p2) < count(p1)) p1 = p2 # try condensing f2 = -> if (car(p1) != symbol(ADD)) return push(p1) Condense() p2 = pop() if (count(p2) <= count(p1)) p1 = p2 # this simplifies forms like (A-B) / (B-A) f3 = -> push(p1) rationalize() negate() rationalize() negate() rationalize() p2 = pop() if (count(p2) < count(p1)) p1 = p2 f10 = -> carp1 = car(p1) miao = cdr(p1) if ( carp1 == symbol(MULTIPLY) || isinnerordot(p1)) # both operands a transpose? if (car(car(cdr(p1))) == symbol(TRANSPOSE)) and (car(car(cdr(cdr(p1)))) == symbol(TRANSPOSE)) if DEBUG then console.log "maybe collecting a transpose " + p1 a = cadr(car(cdr(p1))) b = cadr(car(cdr(cdr(p1)))) if carp1 == symbol(MULTIPLY) push(a) push(b) multiply() else if isinnerordot(p1) push(b) push(a) inner() push_integer(1) push_integer(2) originalexpanding = expanding expanding = false transpose() expanding = originalexpanding p2 = pop() if (count(p2) < count(p1)) p1 = p2 if DEBUG then console.log "collecting a transpose " + p2 # try expanding denominators f4 = -> if (iszero(p1)) return push(p1) rationalize() inverse() rationalize() inverse() rationalize() p2 = pop() if (count(p2) < count(p1)) p1 = p2 # simplifies trig forms simplify_trig = -> save() p1 = pop() f5() push(p1) restore() f5 = -> if (Find(p1, symbol(SIN)) == 0 && Find(p1, symbol(COS)) == 0) return p2 = p1 trigmode = 1 push(p2) Eval() p3 = pop() trigmode = 2 push(p2) Eval() p4 = pop() trigmode = 0 if (count(p4) < count(p3) || nterms(p4) < nterms(p3)) p3 = p4 if (count(p3) < count(p1) || nterms(p3) < nterms(p1)) p1 = p3 # if it's a sum then try to simplify each term f9 = -> if (car(p1) != symbol(ADD)) return push_integer(0) p2 = cdr(p1) while (iscons(p2)) push(car(p2)) simplify() add() p2 = cdr(p2) p2 = pop() if (count(p2) < count(p1)) p1 = p2 # things like 6*(cos(2/9*pi)+i*sin(2/9*pi)) # where we have sin and cos, those might start to # look better in clock form i.e. 6*(-1)^(2/9) simplify_rectToClock = -> #debugger if (Find(p1, symbol(SIN)) == 0 && Find(p1, symbol(COS)) == 0) return push(p1) Eval() clockform() p2 = pop(); # put new (hopefully simplified expr) in p2 if DEBUG then console.log "before simplification clockform: " + p1 + " after: " + p2 if (count(p2) < count(p1)) p1 = p2 simplify_polarRect = -> push(p1) polarRectAMinusOneBase() Eval() p2 = pop(); # put new (hopefully simplified expr) in p2 if (count(p2) < count(p1)) p1 = p2 polarRectAMinusOneBase = -> save() p1 = pop() if isimaginaryunit(p1) push(p1) restore() return if (equal(car(p1), symbol(POWER)) and isminusone(cadr(p1)) ) # base we just said is minus 1 push(one) negate() # exponent push(caddr(p1)) polarRectAMinusOneBase() power() # try to simplify it using polar and rect polar() rect() else if (iscons(p1)) h = tos while (iscons(p1)) #console.log("recursing on: " + car(p1).toString()) push(car(p1)) polarRectAMinusOneBase() #console.log("...transformed into: " + stack[tos-1].toString()) p1 = cdr(p1) list(tos - h) else push(p1) restore() return nterms = (p) -> if (car(p) != symbol(ADD)) return 1 else return length(p) - 1 simplify_nested_radicals = -> if recursionLevelNestedRadicalsRemoval > 0 if DEBUG then console.log("denesting bailing out because of too much recursion") return false push(p1) somethingSimplified = take_care_of_nested_radicals() # in this paragraph we check whether we can collect # common factors without complicating the expression # in particular we want to avoid # collecting radicals like in this case where # we collect sqrt(2): # 2-2^(1/2) into 2^(1/2)*(-1+2^(1/2)) # but we do like to collect other non-radicals e.g. # 17/2+3/2*5^(1/2) into 1/2*(17+3*5^(1/2)) # so what we do is we count the powers and we check # which version has the least number of them. simplificationWithoutCondense = stack[tos-1] prev_expanding = expanding expanding = 0 yycondense() expanding = prev_expanding simplificationWithCondense = pop() #console.log("occurrences of powers in " + simplificationWithoutCondense + " :" + countOccurrencesOfSymbol(symbol(POWER),simplificationWithoutCondense)) #console.log("occurrences of powers in " + simplificationWithCondense + " :" + countOccurrencesOfSymbol(symbol(POWER),simplificationWithCondense)) if (countOccurrencesOfSymbol(symbol(POWER),simplificationWithoutCondense) < countOccurrencesOfSymbol(symbol(POWER),simplificationWithCondense)) push(simplificationWithoutCondense) else push(simplificationWithCondense) # we got out result, wrap up p1 = pop() return somethingSimplified take_care_of_nested_radicals = -> if recursionLevelNestedRadicalsRemoval > 0 if DEBUG then console.log("denesting bailing out because of too much recursion") return false save() p1 = pop() #console.log("take_care_of_nested_radicals p1: " + p1.toString()) if equal(car(p1), symbol(POWER)) #console.log("ok it's a power ") base = cadr(p1) exponent = caddr(p1) #console.log("possible double radical base: " + base) #console.log("possible double radical exponent: " + exponent) if !isminusone(exponent) and equal(car(base), symbol(ADD)) and isfraction(exponent) and (equalq(exponent,1,3) or equalq(exponent,1,2)) #console.log("ok there is a radix with a term inside") firstTerm = cadr(base) push(firstTerm) take_care_of_nested_radicals() pop() secondTerm = caddr(base) push(secondTerm) take_care_of_nested_radicals() pop() #console.log("possible double radical term1: " + firstTerm) #console.log("possible double radical term2: " + secondTerm) numberOfTerms = 0 countingTerms = base while (cdr(countingTerms) != symbol(NIL)) numberOfTerms++ countingTerms = cdr(countingTerms) #console.log("number of terms: " + numberOfTerms) if numberOfTerms > 2 #console.log("too many terms under outer radix ") push(p1) restore() return false # list here all the factors commonInnerExponent = null commonBases = [] termsThatAreNotPowers = [] if (car(secondTerm) == symbol(MULTIPLY)) # product of factors secondTermFactor = cdr(secondTerm) if iscons(secondTermFactor) while (iscons(secondTermFactor)) #console.log("second term factor BIS: " + car(secondTermFactor).toString()) potentialPower = car(secondTermFactor) if (car(potentialPower) == symbol(POWER)) innerbase = cadr(potentialPower) innerexponent = caddr(potentialPower) if equalq(innerexponent,1,2) #console.log("tackling double radical 1: " + p1.toString()) if !commonInnerExponent? commonInnerExponent = innerexponent commonBases.push(innerbase) else if equal(innerexponent, commonInnerExponent) #console.log("common base: " + innerbase.toString()) commonBases.push(innerbase) else #console.log("no common bases here ") #console.log("this one is a power base: " + innerbase + " , exponent: " + innerexponent) else termsThatAreNotPowers.push(potentialPower) secondTermFactor = cdr(secondTermFactor) else if (car(secondTerm) == symbol(POWER)) innerbase = cadr(secondTerm) innerexponent = caddr(secondTerm) if !commonInnerExponent? and equalq(innerexponent,1,2) #console.log("tackling double radical 2: " + p1.toString()) commonInnerExponent = innerexponent commonBases.push(innerbase) if commonBases.length == 0 push(p1) restore() return false A = firstTerm #console.log("A: " + A.toString()) push_integer(1) for i in commonBases push(i) multiply() #console.log("basis with common exponent: " + i.toString()) C = pop() #console.log("C: " + C.toString()) push_integer(1) for i in termsThatAreNotPowers push(i) multiply() #console.log("terms that are not powers: " + i.toString()) B = pop() #console.log("B: " + B.toString()) if equalq(exponent,1,3) push(A) negate() push(C) multiply() push(B) divide() # 4th coeff #console.log("constant coeff " + stack[tos-1].toString()) checkSize = pop() push(checkSize) real() yyfloat() if Math.abs(pop().d) > Math.pow(2, 32) push(p1) restore() return false push(checkSize) push_integer(3) push(C) multiply() # 3rd coeff #console.log("next coeff " + stack[tos-1].toString()) checkSize = pop() push(checkSize) real() yyfloat() if Math.abs(pop().d) > Math.pow(2, 32) pop() push(p1) restore() return false push(checkSize) push(symbol(SECRETX)) multiply() push_integer(-3) push(A) multiply() push(B) divide() # 2nd coeff checkSize = pop() push(checkSize) real() yyfloat() if Math.abs(pop().d) > Math.pow(2, 32) pop() pop() push(p1) restore() return false push(checkSize) #console.log("next coeff " + stack[tos-1].toString()) push(symbol(SECRETX)) push_integer(2) power() multiply() push_integer(1) # 1st coeff #console.log("next coeff " + stack[tos-1].toString()) push(symbol(SECRETX)) push_integer(3) power() multiply() add() add() add() else if equalq(exponent,1,2) push(C) # 3th coeff checkSize = pop() push(checkSize) real() yyfloat() if Math.abs(pop().d) > Math.pow(2, 32) push(p1) restore() return false push(checkSize) #console.log("constant coeff " + stack[tos-1].toString()) push_integer(-2) push(A) multiply() push(B) divide() # 2nd coeff checkSize = pop() push(checkSize) real() yyfloat() if Math.abs(pop().d) > Math.pow(2, 32) pop() push(p1) restore() return false push(checkSize) #console.log("next coeff " + stack[tos-1].toString()) push(symbol(SECRETX)) multiply() push_integer(1) # 1st coeff #console.log("next coeff " + stack[tos-1].toString()) push(symbol(SECRETX)) push_integer(2) power() multiply() add() add() #console.log("whole polynomial: " + stack[tos-1].toString()) push(symbol(SECRETX)) recursionLevelNestedRadicalsRemoval++ #console.log("invoking roots at recursion level: " + recursionLevelNestedRadicalsRemoval) roots() recursionLevelNestedRadicalsRemoval-- if equal(stack[tos-1], symbol(NIL)) if DEBUG then console.log("roots bailed out because of too much recursion") pop() push(p1) restore() return false #console.log("all solutions: " + stack[tos-1].toString()) # exclude the solutions with radicals possibleSolutions = [] for eachSolution in stack[tos-1].tensor.elem if !Find(eachSolution, symbol(POWER)) possibleSolutions.push(eachSolution) pop() # popping the tensor with the solutions #console.log("possible solutions: " + possibleSolutions.toString()) if possibleSolutions.length == 0 push(p1) restore() return false possibleRationalSolutions = [] realOfpossibleRationalSolutions = [] #console.log("checking the one with maximum real part ") for i in possibleSolutions push(i) real() yyfloat() possibleRationalSolutions.push(i) realOfpossibleRationalSolutions.push(pop().d) whichRationalSolution = realOfpossibleRationalSolutions.indexOf(Math.max.apply(Math, realOfpossibleRationalSolutions)) SOLUTION = possibleRationalSolutions[whichRationalSolution] #console.log("picked solution: " + SOLUTION) ### #possibleNewExpressions = [] #realOfPossibleNewExpressions = [] # pick the solution which cubic root has no radicals lowercase_b = null for SOLUTION in possibleSolutions console.log("testing solution: " + SOLUTION.toString()) debugger if equalq(exponent,1,3) push(A) push(SOLUTION) push_integer(3) power() push_integer(3) push(C) multiply() push(SOLUTION) multiply() add() divide() console.log("argument of cubic root: " + stack[tos-1].toString()) push_rational(1,3) power() else if equalq(exponent,1,2) push(A) push(SOLUTION) push_integer(2) power() push(C) add() divide() console.log("argument of cubic root: " + stack[tos-1].toString()) push_rational(1,2) power() console.log("b is: " + stack[tos-1].toString()) lowercase_b = pop() if !Find(lowercase_b, symbol(POWER)) break ### if equalq(exponent,1,3) push(A) push(SOLUTION) push_integer(3) power() push_integer(3) push(C) multiply() push(SOLUTION) multiply() add() divide() #console.log("argument of cubic root: " + stack[tos-1].toString()) push_rational(1,3) power() else if equalq(exponent,1,2) push(A) push(SOLUTION) push_integer(2) power() push(C) add() divide() #console.log("argument of cubic root: " + stack[tos-1].toString()) push_rational(1,2) power() #console.log("b is: " + stack[tos-1].toString()) lowercase_b = pop() if !lowercase_b? push(p1) restore() return false push(lowercase_b) push(SOLUTION) multiply() if equalq(exponent,1,3) #console.log("a is: " + stack[tos-1].toString()) lowercase_a = pop() push(lowercase_b) push(C) push_rational(1,2) power() multiply() push(lowercase_a) add() simplify() else if equalq(exponent,1,2) #console.log("a could be: " + stack[tos-1].toString()) lowercase_a = pop() push(lowercase_b) push(C) push_rational(1,2) power() multiply() push(lowercase_a) add() simplify() possibleNewExpression = pop() #console.log("verifying if " + possibleNewExpression + " is positive") push(possibleNewExpression) real() yyfloat() possibleNewExpressionValue = pop() if !isnegativenumber(possibleNewExpressionValue) #console.log("... it is positive") push(possibleNewExpression) else #console.log("... it is NOT positive") push(lowercase_b) negate() lowercase_b = pop() push(lowercase_a) negate() lowercase_a = pop() push(lowercase_b) push(C) push_rational(1,2) power() multiply() push(lowercase_a) add() simplify() # possibleNewExpression is now at top of stack #console.log("potential new expression: " + stack[tos-1].toString()) p1 = pop() #newExpression = pop() #debugger #push(newExpression) #real() #yyfloat() #possibleNewExpressions.push(newExpression) #realOfPossibleNewExpressions.push(pop().d) #whichExpression = realOfPossibleNewExpressions.indexOf(Math.max.apply(Math, realOfPossibleNewExpressions)) #p1 = possibleNewExpressions[whichExpression] #console.log("final new expression: " + p1.toString()) push(p1) restore() return true else push(p1) restore() return false else if (iscons(p1)) h = tos anyRadicalSimplificationWorked = false while (iscons(p1)) #console.log("recursing on: " + car(p1).toString()) push(car(p1)) anyRadicalSimplificationWorked = anyRadicalSimplificationWorked or take_care_of_nested_radicals() #console.log("...transformed into: " + stack[tos-1].toString()) p1 = cdr(p1) list(tos - h) restore() return anyRadicalSimplificationWorked else push(p1) restore() return false throw new Error("control flow should never reach here")