diff options
Diffstat (limited to 'Master/texmf-dist/tex/lualatex/luacas/calculus/luacas-derivativeexpression.lua')
-rw-r--r-- | Master/texmf-dist/tex/lualatex/luacas/calculus/luacas-derivativeexpression.lua | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/lualatex/luacas/calculus/luacas-derivativeexpression.lua b/Master/texmf-dist/tex/lualatex/luacas/calculus/luacas-derivativeexpression.lua new file mode 100644 index 00000000000..b757759c4c9 --- /dev/null +++ b/Master/texmf-dist/tex/lualatex/luacas/calculus/luacas-derivativeexpression.lua @@ -0,0 +1,265 @@ +--- @class DerivativeExpression +--- An expression for a single-variable derivative of an expression. +--- @field symbol SymbolExpression +--- @field expression Expression +DerivativeExpression = {} +__DerivativeExpression = {} + +---------------------------- +-- Instance functionality -- +---------------------------- + +-- Creates a new single-variable derivative operation with the given symbol and expression. +--- @param expression Expression +--- @param symbol Symbol +--- @return DerivativeExpression +function DerivativeExpression:new(expression, symbol) + local o = {} + local __o = Copy(__ExpressionOperations) + + o.expression = Copy(expression) + o.symbol = symbol or SymbolExpression("x") + + __o.__index = DerivativeExpression + __o.__tostring = function(a) + return '(d/d' .. tostring(a.symbol) .. " " .. tostring(a.expression) .. ')' + end + __o.__eq = function(a, b) + -- This shouldn't be needed, since __eq should only fire if both metamethods have the same function, but for some reason Lua always runs this anyway + if not b:type() == DerivativeExpression then + return false + end + return a.symbol == b.symbol and a.expression == b.expression + end + o = setmetatable(o, __o) + + return o +end + +--- @return Expression +function DerivativeExpression:evaluate() + local exp = self.expression + + -- The derivative of a constant is 0 + if exp:isconstant() then + return Integer.zero() + end + + -- The derivative of a symbol is either 1 or 0 + if exp:type() == SymbolExpression then + if self.symbol == exp then + return Integer.one() + end + return Integer.zero() + end + + -- Chain rule for arbitrary functions + + if exp:type() == FunctionExpression then + local results = {} + for index,expression in ipairs(exp.expressions) do + local dout = FunctionExpression(exp.name,exp.expressions,exp.derivatives) + dout.variables = exp.variables + dout.derivatives[index] = dout.derivatives[index]+Integer.one() + local dinn = DerivativeExpression(expression,self.symbol):evaluate() + results[index] = dout*dinn + end + return BinaryOperation(BinaryOperation.ADD,results):autosimplify() + end + + --if exp:type() == FunctionExpression then + -- local results = {} + -- for index,expression in ipairs(exp.expressions) do + -- local inn = DerivativeExpression(expression,self.symbol):autosimplify() + -- local out = Copy(exp) + -- out.orders[index] = out.orders[index] + Integer.one() + -- local result = inn*out + -- table.insert(results,result) + -- end + -- return BinaryOperation(BinaryOperation.ADD,results):autosimplify() + --end + + --if exp:type() == FunctionExpression then + -- if exp.expressions[2] then + -- return self + -- end + -- return DerivativeExpression(exp.expressions[1], self.symbol) * FunctionExpression(exp.name, exp.expressions, exp.orders[1] + Integer.one(), exp.variables[1]):autosimplify() + --end + + -- Chain rule for trig functions + if exp:type() == TrigExpression then + local internal = DerivativeExpression(exp.expression, self.symbol) + + if exp.name == "sin" then + return (internal * COS(exp.expression)):autosimplify() + end + if exp.name == "cos" then + return (internal * -SIN(exp.expression)):autosimplify() + end + if exp.name == "tan" then + return (internal * SEC(exp.expression)^Integer(2)):autosimplify() + end + if exp.name == "csc" then + return (internal * -CSC(exp.expression)*COT(exp.expression)):autosimplify() + end + if exp.name == "sec" then + return (internal * -SEC(exp.expression)*TAN(exp.expression)):autosimplify() + end + if exp.name == "cot" then + return (internal * -CSC(exp.expression)^Integer(2)):autosimplify() + end + if exp.name == "arcsin" then + return (internal / (Integer(1)-exp.expression^Integer(2))^(Integer(1)/Integer(2))):autosimplify() + end + if exp.name == "arccos" then + return (-internal / (Integer(1)-exp.expression^Integer(2))^(Integer(1)/Integer(2))):autosimplify() + end + if exp.name == "arctan" then + return (internal / (Integer(1)+exp.expression^Integer(2))):autosimplify() + end + if exp.name == "arccsc" then + return (-internal / (ABS(exp.expression) * (Integer(1)-exp.expression^Integer(2))^(Integer(1)/Integer(2)))):autosimplify() + end + if exp.name == "arcsec" then + return (internal / (ABS(exp.expression) * (Integer(1)-exp.expression^Integer(2))^(Integer(1)/Integer(2)))):autosimplify() + end + if exp.name == "arccot" then + return (-internal / (Integer(1)+exp.expression^Integer(2))):autosimplify() + end + end + + -- TODO: Piecewise functions + if self:type() == AbsExpression then + return DerivativeExpression(self.expression, self.symbol):autosimplify() + end + + -- Uses linearity of derivatives to evaluate sum expressions + if exp.operation == BinaryOperation.ADD then + local parts = {} + for i, expression in pairs(exp.expressions) do + parts[i] = DerivativeExpression(expression, self.symbol) + end + return BinaryOperation(BinaryOperation.ADD, parts):autosimplify() + end + + -- Uses product rule to evaluate product expressions + if exp.operation == BinaryOperation.MUL then + local sums = {} + for i, expression in pairs(exp.expressions) do + local products = {} + for j, innerexpression in pairs(exp.expressions) do + if i ~= j then + products[j] = innerexpression + else + products[j] = DerivativeExpression(innerexpression, self.symbol) + end + end + sums[i] = BinaryOperation(BinaryOperation.MUL, products) + end + return BinaryOperation(BinaryOperation.ADD, sums):autosimplify() + end + + -- Uses the generalized power rule to evaluate power expressions + if exp.operation == BinaryOperation.POW then + local base = exp.expressions[1] + local exponent = exp.expressions[2] + + return BinaryOperation.MULEXP({ + BinaryOperation.POWEXP({base, exponent}), + BinaryOperation.ADDEXP({ + BinaryOperation.MULEXP({ + DD(base, self.symbol), + BinaryOperation.DIVEXP({exponent, base})}), + BinaryOperation.MULEXP({ + DD(exponent, self.symbol), + LN(base)})}) + }):autosimplify() + end + + if exp:type() == Logarithm then + local base = exp.base + local expression = exp.expression + + return BinaryOperation.SUBEXP({ + BinaryOperation.DIVEXP({DD(expression, self.symbol), + BinaryOperation.MULEXP({expression, LN(base)})}), + + BinaryOperation.DIVEXP({ + BinaryOperation.MULEXP({LN(expression), DD(base, self.symbol)}), + BinaryOperation.MULEXP({BinaryOperation.POWEXP({LN(base), Integer(2)}), base}) + }) + + }):autosimplify() + end + + return self +end + +--- @return Expression +function DerivativeExpression:autosimplify() + return DerivativeExpression(self.expression:autosimplify(), self.symbol):evaluate() +end + +--- @return table<number, Expression> +function DerivativeExpression:subexpressions() + return {self.expression} +end + +--- @param subexpressions table<number, Expression> +--- @return DerivativeExpression +function DerivativeExpression:setsubexpressions(subexpressions) + return DerivativeExpression(subexpressions[1], self.symbol) +end + +-- function DerivativeExpression:freeof(symbol) +-- return self.symbol.freeof(symbol) and self.expression:freeof(symbol) +-- end + +-- Substitutes each expression for a new one. +-- function DerivativeExpression:substitute(map) +-- for expression, replacement in pairs(map) do +-- if self == expression then +-- return replacement +-- end +-- end +-- -- Typically, we only perform substitution on autosimplified expressions, so this won't get called. May give strange results, i.e., +-- -- substituting and then evaluating the derivative may not return the same thing as evaluating the derivative and then substituting. +-- return DerivativeExpression(self.expression:substitute(map), self.symbol) +-- end + +--- @param other Expression +--- @return boolean +function DerivativeExpression:order(other) + if other:type() == IntegralExpression then + return true + end + + if other:type() ~= DerivativeExpression then + return false + end + + if self.symbol ~= other.symbol then + return self.symbol:order(other.symbol) + end + + return self.expression:order(other.expression) +end + +--- @return string +function DerivativeExpression:tolatex() + return '\\frac{d}{d' .. self.symbol:tolatex() .. '}\\left(' .. self.expression:tolatex() .. '\\right)' +end + +----------------- +-- Inheritance -- +----------------- +__DerivativeExpression.__index = CompoundExpression +__DerivativeExpression.__call = DerivativeExpression.new +DerivativeExpression = setmetatable(DerivativeExpression, __DerivativeExpression) + +---------------------- +-- Static constants -- +---------------------- +DD = function(expression, symbol) + return DerivativeExpression(expression, symbol) +end
\ No newline at end of file |