mirror of
https://github.com/josdejong/mathjs.git
synced 2026-01-18 14:59:29 +00:00
722 lines
25 KiB
JavaScript
722 lines
25 KiB
JavaScript
'use strict';
|
|
|
|
function factory (type, config, load, typed) {
|
|
var parse = load(require('../../expression/parse'));
|
|
var simplify = load(require('./simplify'));
|
|
var ConstantNode = load(require('../../expression/node/ConstantNode'));
|
|
var FunctionNode = load(require('../../expression/node/FunctionNode'));
|
|
var OperatorNode = load(require('../../expression/node/OperatorNode'));
|
|
var ParenthesisNode = load(require('../../expression/node/ParenthesisNode'));
|
|
var SymbolNode = load(require('../../expression/node/SymbolNode'));
|
|
|
|
/**
|
|
* Takes the derivative of an expression expressed in parser Nodes.
|
|
* The derivative will be taken over the supplied variable in the
|
|
* second parameter. If there are multiple variables in the expression,
|
|
* it will return a partial derivative.
|
|
*
|
|
* This uses rules of differentiation which can be found here:
|
|
*
|
|
* - [Differentiation rules (Wikipedia)](http://en.wikipedia.org/wiki/Differentiation_rules)
|
|
*
|
|
* Syntax:
|
|
*
|
|
* derivative(expr, variable)
|
|
* derivative(expr, variable, options)
|
|
*
|
|
* Examples:
|
|
*
|
|
* math.derivative('x^2', 'x'); // Node {2 * x}
|
|
* math.derivative('x^2', 'x', {simplify: false}); // Node {2 * 1 * x ^ (2 - 1)
|
|
* math.derivative('sin(2x)', 'x')); // Node {2 * cos(2 * x)}
|
|
* math.derivative('2*x', 'x').eval(); // number 2
|
|
* math.derivative('x^2', 'x').eval({x: 4}); // number 8
|
|
* var f = math.parse('x^2');
|
|
* var x = math.parse('x');
|
|
* math.derivative(f, x); // Node {2 * x}
|
|
*
|
|
* See also:
|
|
*
|
|
* simplify, parse, eval
|
|
*
|
|
* @param {Node | string} expr The expression to differentiate
|
|
* @param {SymbolNode | string} variable The variable over which to differentiate
|
|
* @param {{simplify: boolean}} [options]
|
|
* There is one option available, `simplify`, which
|
|
* is true by default. When false, output will not
|
|
* be simplified.
|
|
* @return {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} The derivative of `expr`
|
|
*/
|
|
var derivative = typed('derivative', {
|
|
'Node, SymbolNode, Object': function (expr, variable, options) {
|
|
var constNodes = {};
|
|
constTag(constNodes, expr, variable.name);
|
|
var res = _derivative(expr, constNodes);
|
|
return options.simplify ? simplify(res) : res;
|
|
},
|
|
'Node, SymbolNode': function (expr, variable) {
|
|
return derivative(expr, variable, {simplify: true})
|
|
},
|
|
|
|
'string, SymbolNode': function (expr, variable) {
|
|
return derivative(parse(expr), variable)
|
|
},
|
|
'string, SymbolNode, Object': function (expr, variable, options) {
|
|
return derivative(parse(expr), variable, options)
|
|
},
|
|
|
|
'string, string': function (expr, variable) {
|
|
return derivative(parse(expr), parse(variable))
|
|
},
|
|
'string, string, Object': function (expr, variable, options) {
|
|
return derivative(parse(expr), parse(variable), options)
|
|
},
|
|
|
|
'Node, string': function (expr, variable) {
|
|
return derivative(expr, parse(variable))
|
|
},
|
|
'Node, string, Object': function (expr, variable, options) {
|
|
return derivative(expr, parse(variable), options)
|
|
}
|
|
|
|
// TODO: replace the 8 signatures above with 4 as soon as typed-function supports optional arguments
|
|
|
|
/* TODO: implement and test syntax with order of derivatives -> implement as an option {order: number}
|
|
'Node, SymbolNode, ConstantNode': function (expr, variable, {order}) {
|
|
var res = expr;
|
|
for (var i = 0; i < order; i++) {
|
|
var constNodes = {};
|
|
constTag(constNodes, expr, variable.name);
|
|
res = _derivative(res, constNodes);
|
|
}
|
|
return res;
|
|
}
|
|
*/
|
|
});
|
|
|
|
derivative._simplify = true
|
|
|
|
derivative.toTex = function(deriv) {
|
|
return _derivTex.apply(null, deriv.args);
|
|
}
|
|
|
|
var _derivTex = typed('_derivTex', {
|
|
'Node, SymbolNode': function (expr, x) {
|
|
return _derivTex(expr.toString(), x.toString(), 1);
|
|
},
|
|
'Node, SymbolNode, ConstantNode': function (expr, x, order) {
|
|
return _derivTex(expr.toString(), x.name, order.value);
|
|
},
|
|
'string, string, number': function (expr, x, order) {
|
|
var d;
|
|
if (order === 1) {
|
|
d = "{d\\over d" + x + "}";
|
|
}
|
|
else {
|
|
d = "{d^{" + order + "}\\over d" + x + "^{" + order + "}}";
|
|
}
|
|
return d + "\\left[" + expr + "\\right]"
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Does a depth-first search on the expression tree to identify what Nodes
|
|
* are constants (e.g. 2 + 2), and stores the ones that are constants in
|
|
* constNodes. Classification is done as follows:
|
|
*
|
|
* 1. ConstantNodes are constants.
|
|
* 2. If there exists a SymbolNode, of which we are differentiating over,
|
|
* in the subtree it is not constant.
|
|
*
|
|
* @param {Object} constNodes Holds the nodes that are constant
|
|
* @param {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} node
|
|
* @param {string} varName Variable that we are differentiating
|
|
* @return {boolean} if node is constant
|
|
*/
|
|
// TODO: can we rewrite constTag into a pure function?
|
|
var constTag = typed('constTag', {
|
|
'Object, ConstantNode, string': function (constNodes, node) {
|
|
return constNodes[node] = true;
|
|
},
|
|
|
|
'Object, SymbolNode, string': function (constNodes, node, varName) {
|
|
// Treat other variables like constants. For reasoning, see:
|
|
// https://en.wikipedia.org/wiki/Partial_derivative
|
|
if (node.name != varName) {
|
|
return constNodes[node] = true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
'Object, ParenthesisNode, string': function (constNodes, node, varName) {
|
|
return constTag(constNodes, node.content, varName);
|
|
},
|
|
|
|
'Object, FunctionAssignmentNode, string': function (constNodes, node, varName) {
|
|
if (node.params.indexOf(varName) == -1) {
|
|
return constNodes[node] = true;
|
|
}
|
|
return constTag(constNodes, node.expr, varName);
|
|
},
|
|
|
|
'Object, FunctionNode | OperatorNode, string': function (constNodes, node, varName) {
|
|
if (node.args.length != 0) {
|
|
var isConst = constTag(constNodes, node.args[0], varName);
|
|
for (var i = 1; i < node.args.length; ++i) {
|
|
isConst = constTag(constNodes, node.args[i], varName) && isConst;
|
|
}
|
|
|
|
if (isConst) {
|
|
return constNodes[node] = true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Applies differentiation rules.
|
|
*
|
|
* @param {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} node
|
|
* @param {Object} constNodes Holds the nodes that are constant
|
|
* @return {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} The derivative of `expr`
|
|
*/
|
|
var _derivative = typed('_derivative', {
|
|
'ConstantNode, Object': function (node) {
|
|
return new ConstantNode('0', node.valueType);
|
|
},
|
|
|
|
'SymbolNode, Object': function (node, constNodes) {
|
|
if (constNodes[node] !== undefined) {
|
|
return new ConstantNode('0', config.number);
|
|
}
|
|
return new ConstantNode('1', config.number);
|
|
},
|
|
|
|
'ParenthesisNode, Object': function (node, constNodes) {
|
|
return new ParenthesisNode(_derivative(node.content, constNodes));
|
|
},
|
|
|
|
'FunctionAssignmentNode, Object': function (node, constNodes) {
|
|
if (constNodes[node] !== undefined) {
|
|
return new ConstantNode('0', config.number);
|
|
}
|
|
return _derivative(node.expr, constNodes);
|
|
},
|
|
|
|
'FunctionNode, Object': function (node, constNodes) {
|
|
if (node.args.length != 1) {
|
|
funcArgsCheck(node);
|
|
}
|
|
|
|
if (constNodes[node] !== undefined) {
|
|
return new ConstantNode('0', config.number);
|
|
}
|
|
|
|
var arg1 = node.args[0];
|
|
var arg2;
|
|
|
|
var div = false; // is output a fraction?
|
|
var negative = false; // is output negative?
|
|
|
|
var funcDerivative;
|
|
switch (node.name) {
|
|
case 'cbrt':
|
|
// d/dx(cbrt(x)) = 1 / (3x^(2/3))
|
|
div = true;
|
|
funcDerivative = new OperatorNode('*', 'multiply', [
|
|
new ConstantNode('3', config.number),
|
|
new OperatorNode('^', 'pow', [
|
|
arg1,
|
|
new OperatorNode('/', 'divide', [
|
|
new ConstantNode('2', config.number),
|
|
new ConstantNode('3', config.number)
|
|
])
|
|
])
|
|
]);
|
|
break;
|
|
case 'sqrt':
|
|
case 'nthRoot':
|
|
// d/dx(sqrt(x)) = 1 / (2*sqrt(x))
|
|
if (node.args.length == 1) {
|
|
div = true;
|
|
funcDerivative = new OperatorNode('*', 'multiply', [
|
|
new ConstantNode('2', config.number),
|
|
new FunctionNode('sqrt', [arg1])
|
|
]);
|
|
break;
|
|
}
|
|
|
|
// Rearrange from nthRoot(x, a) -> x^(1/a)
|
|
arg2 = new OperatorNode('/', 'divide', [
|
|
new ConstantNode('1', config.number),
|
|
node.args[1]
|
|
]);
|
|
|
|
// Is a variable?
|
|
constNodes[arg2] = constNodes[node.args[1]];
|
|
|
|
return _derivative(new OperatorNode('^', 'pow', [arg1, arg2]), constNodes);
|
|
case 'log10':
|
|
arg2 = new ConstantNode('10', config.number);
|
|
case 'log':
|
|
if (!arg2 && node.args.length == 1) {
|
|
// d/dx(log(x)) = 1 / x
|
|
funcDerivative = arg1.clone();
|
|
} else if (arg2 || constNodes[node.args[1]] !== undefined) {
|
|
// d/dx(log(x, c)) = 1 / (x*ln(c))
|
|
funcDerivative = new OperatorNode('*', 'multiply', [
|
|
arg1.clone(),
|
|
new FunctionNode('log', [arg2 || node.args[1]])
|
|
]);
|
|
} else {
|
|
// d/dx(log(f(x), g(x))) = d/dx(log(f(x)) / log(g(x)))
|
|
return _derivative(new OperatorNode('/', 'divide', [
|
|
new FunctionNode('log', [arg1]),
|
|
new FunctionNode('log', [node.args[1]])
|
|
]), constNodes);
|
|
}
|
|
|
|
div = true;
|
|
break;
|
|
case 'exp':
|
|
// d/dx(e^x) = e^x
|
|
funcDerivative = new FunctionNode('exp', [arg1.clone()]);
|
|
break;
|
|
case 'sin':
|
|
// d/dx(sin(x)) = cos(x)
|
|
funcDerivative = new FunctionNode('cos', [arg1.clone()]);
|
|
break;
|
|
case 'cos':
|
|
// d/dx(cos(x)) = -sin(x)
|
|
funcDerivative = new OperatorNode('-', 'unaryMinus', [
|
|
new FunctionNode('sin', [arg1.clone()])
|
|
]);
|
|
break;
|
|
case 'tan':
|
|
// d/dx(tan(x)) = sec(x)^2
|
|
funcDerivative = new OperatorNode('^', 'pow', [
|
|
new FunctionNode('sec', [arg1.clone()]),
|
|
new ConstantNode('2', config.number)
|
|
]);
|
|
break;
|
|
case 'sec':
|
|
// d/dx(sec(x)) = sec(x)tan(x)
|
|
funcDerivative = new OperatorNode('*', 'multiply', [
|
|
node,
|
|
new FunctionNode('tan', [arg1.clone()])
|
|
]);
|
|
break;
|
|
case 'csc':
|
|
// d/dx(csc(x)) = -csc(x)cot(x)
|
|
negative = true;
|
|
funcDerivative = new OperatorNode('*', 'multiply', [
|
|
node,
|
|
new FunctionNode('cot', [arg1.clone()])
|
|
]);
|
|
break;
|
|
case 'cot':
|
|
// d/dx(cot(x)) = -csc(x)^2
|
|
negative = true;
|
|
funcDerivative = new OperatorNode('^', 'pow', [
|
|
new FunctionNode('csc', [arg1.clone()]),
|
|
new ConstantNode('2', config.number)
|
|
]);
|
|
break;
|
|
case 'asin':
|
|
// d/dx(asin(x)) = 1 / sqrt(1 - x^2)
|
|
div = true;
|
|
funcDerivative = new FunctionNode('sqrt', [
|
|
new OperatorNode('-', 'subtract', [
|
|
new ConstantNode('1', config.number),
|
|
new OperatorNode('^', 'pow', [
|
|
arg1.clone(),
|
|
new ConstantNode('2', config.number)
|
|
])
|
|
])
|
|
]);
|
|
break;
|
|
case 'acos':
|
|
// d/dx(acos(x)) = -1 / sqrt(1 - x^2)
|
|
div = true;
|
|
negative = true;
|
|
funcDerivative = new FunctionNode('sqrt', [
|
|
new OperatorNode('-', 'subtract', [
|
|
new ConstantNode('1', config.number),
|
|
new OperatorNode('^', 'pow', [
|
|
arg1.clone(),
|
|
new ConstantNode('2', config.number)
|
|
])
|
|
])
|
|
]);
|
|
break;
|
|
case 'atan':
|
|
// d/dx(atan(x)) = 1 / (x^2 + 1)
|
|
div = true;
|
|
funcDerivative = new OperatorNode('+', 'add', [
|
|
new OperatorNode('^', 'pow', [
|
|
arg1.clone(),
|
|
new ConstantNode('2', config.number)
|
|
]),
|
|
new ConstantNode('1', config.number)
|
|
]);
|
|
break;
|
|
case 'asec':
|
|
// d/dx(asec(x)) = 1 / (|x|*sqrt(x^2 - 1))
|
|
div = true;
|
|
funcDerivative = new OperatorNode('*', 'multiply', [
|
|
new FunctionNode('abs', [arg1.clone()]),
|
|
new FunctionNode('sqrt', [
|
|
new OperatorNode('-', 'subtract', [
|
|
new OperatorNode('^', 'pow', [
|
|
arg1.clone(),
|
|
new ConstantNode('2', config.number)
|
|
]),
|
|
new ConstantNode('1', config.number)
|
|
])
|
|
])
|
|
]);
|
|
break;
|
|
case 'acsc':
|
|
// d/dx(acsc(x)) = -1 / (|x|*sqrt(x^2 - 1))
|
|
div = true;
|
|
negative = true;
|
|
funcDerivative = new OperatorNode('*', 'multiply', [
|
|
new FunctionNode('abs', [arg1.clone()]),
|
|
new FunctionNode('sqrt', [
|
|
new OperatorNode('-', 'subtract', [
|
|
new OperatorNode('^', 'pow', [
|
|
arg1.clone(),
|
|
new ConstantNode('2', config.number)
|
|
]),
|
|
new ConstantNode('1', config.number)
|
|
])
|
|
])
|
|
]);
|
|
break;
|
|
case 'acot':
|
|
// d/dx(acot(x)) = -1 / (x^2 + 1)
|
|
div = true;
|
|
negative = true;
|
|
funcDerivative = new OperatorNode('+', 'add', [
|
|
new OperatorNode('^', 'pow', [
|
|
arg1.clone(),
|
|
new ConstantNode('2', config.number)
|
|
]),
|
|
new ConstantNode('1', config.number)
|
|
]);
|
|
break;
|
|
case 'sinh':
|
|
// d/dx(sinh(x)) = cosh(x)
|
|
funcDerivative = new FunctionNode('cosh', [arg1.clone()]);
|
|
break;
|
|
case 'cosh':
|
|
// d/dx(cosh(x)) = sinh(x)
|
|
funcDerivative = new FunctionNode('sinh', [arg1.clone()]);
|
|
break;
|
|
case 'tanh':
|
|
// d/dx(tanh(x)) = sech(x)^2
|
|
funcDerivative = new OperatorNode('^', 'pow', [
|
|
new FunctionNode('sech', [arg1.clone()]),
|
|
new ConstantNode('2', config.number)
|
|
]);
|
|
break;
|
|
case 'sech':
|
|
// d/dx(sech(x)) = -sech(x)tanh(x)
|
|
negative = true;
|
|
funcDerivative = new OperatorNode('*', 'multiply', [
|
|
node,
|
|
new FunctionNode('tanh', [arg1.clone()])
|
|
]);
|
|
break;
|
|
case 'csch':
|
|
// d/dx(csch(x)) = -csch(x)coth(x)
|
|
negative = true;
|
|
funcDerivative = new OperatorNode('*', 'multiply', [
|
|
node,
|
|
new FunctionNode('coth', [arg1.clone()])
|
|
]);
|
|
break;
|
|
case 'coth':
|
|
// d/dx(coth(x)) = -csch(x)^2
|
|
negative = true;
|
|
funcDerivative = new OperatorNode('^', 'pow', [
|
|
new FunctionNode('csch', [arg1.clone()]),
|
|
new ConstantNode('2', config.number)
|
|
]);
|
|
break;
|
|
case 'asinh':
|
|
// d/dx(asinh(x)) = 1 / sqrt(x^2 + 1)
|
|
div = true;
|
|
funcDerivative = new FunctionNode('sqrt', [
|
|
new OperatorNode('+', 'add', [
|
|
new OperatorNode('^', 'pow', [
|
|
arg1.clone(),
|
|
new ConstantNode('2', config.number)
|
|
]),
|
|
new ConstantNode('1', config.number)
|
|
])
|
|
]);
|
|
break;
|
|
case 'acosh':
|
|
// d/dx(acosh(x)) = 1 / sqrt(x^2 - 1); XXX potentially only for x >= 1 (the real spectrum)
|
|
div = true;
|
|
funcDerivative = new FunctionNode('sqrt', [
|
|
new OperatorNode('-', 'subtract', [
|
|
new OperatorNode('^', 'pow', [
|
|
arg1.clone(),
|
|
new ConstantNode('2', config.number)
|
|
]),
|
|
new ConstantNode('1', config.number),
|
|
])
|
|
]);
|
|
break;
|
|
case 'atanh':
|
|
// d/dx(atanh(x)) = 1 / (1 - x^2)
|
|
div = true;
|
|
funcDerivative = new OperatorNode('-', 'subtract', [
|
|
new ConstantNode('1', config.number),
|
|
new OperatorNode('^', 'pow', [
|
|
arg1.clone(),
|
|
new ConstantNode('2', config.number)
|
|
])
|
|
]);
|
|
break;
|
|
case 'asech':
|
|
// d/dx(asech(x)) = -1 / (x*sqrt(1 - x^2))
|
|
div = true;
|
|
negative = true;
|
|
funcDerivative = new OperatorNode('*', 'multiply', [
|
|
arg1.clone(),
|
|
new FunctionNode('sqrt', [
|
|
new OperatorNode('-', 'subtract', [
|
|
new ConstantNode('1', config.number),
|
|
new OperatorNode('^', 'pow', [
|
|
arg1.clone(),
|
|
new ConstantNode('2', config.number)
|
|
])
|
|
])
|
|
])
|
|
]);
|
|
break;
|
|
case 'acsch':
|
|
// d/dx(acsch(x)) = -1 / (|x|*sqrt(x^2 + 1))
|
|
div = true;
|
|
negative = true;
|
|
funcDerivative = new OperatorNode('*', 'multiply', [
|
|
new FunctionNode('abs', [arg1.clone()]),
|
|
new FunctionNode('sqrt', [
|
|
new OperatorNode('+', 'add', [
|
|
new OperatorNode('^', 'pow', [
|
|
arg1.clone(),
|
|
new ConstantNode('2', config.number)
|
|
]),
|
|
new ConstantNode('1', config.number)
|
|
])
|
|
])
|
|
]);
|
|
break;
|
|
case 'acoth':
|
|
// d/dx(acoth(x)) = -1 / (1 - x^2)
|
|
div = true;
|
|
negative = true;
|
|
funcDerivative = new OperatorNode('-', 'subtract', [
|
|
new ConstantNode('1', config.number),
|
|
new OperatorNode('^', 'pow', [
|
|
arg1.clone(),
|
|
new ConstantNode('2', config.number)
|
|
])
|
|
]);
|
|
break;
|
|
case 'abs':
|
|
// d/dx(abs(x)) = abs(x)/x
|
|
funcDerivative = new OperatorNode('/', 'divide', [
|
|
new FunctionNode(new SymbolNode('abs'), [arg1.clone()]),
|
|
arg1.clone()
|
|
]);
|
|
break;
|
|
case 'gamma': // Needs digamma function, d/dx(gamma(x)) = gamma(x)digamma(x)
|
|
default: throw new Error('Function "' + node.name + '" not supported by derivative');
|
|
}
|
|
|
|
var op, func;
|
|
if (div) {
|
|
op = '/';
|
|
func = 'divide';
|
|
} else {
|
|
op = '*';
|
|
func = 'multiply';
|
|
}
|
|
|
|
/* Apply chain rule to all functions:
|
|
F(x) = f(g(x))
|
|
F'(x) = g'(x)*f'(g(x)) */
|
|
var chainDerivative = _derivative(arg1, constNodes);
|
|
if (negative) {
|
|
chainDerivative = new OperatorNode('-', 'unaryMinus', [chainDerivative]);
|
|
}
|
|
return new OperatorNode(op, func, [chainDerivative, funcDerivative]);
|
|
},
|
|
|
|
'OperatorNode, Object': function (node, constNodes) {
|
|
if (constNodes[node] !== undefined) {
|
|
return new ConstantNode('0', config.number);
|
|
}
|
|
|
|
var arg1 = node.args[0];
|
|
var arg2 = node.args[1];
|
|
|
|
switch (node.op) {
|
|
case '+':
|
|
case '-':
|
|
// d/dx(+/-f(x)) = +/-f'(x)
|
|
if (node.args.length == 1) {
|
|
return new OperatorNode(node.op, node.fn, [_derivative(arg1, constNodes)]);
|
|
}
|
|
|
|
// Linearity of differentiation, d/dx(f(x) +/- g(x)) = f'(x) +/- g'(x)
|
|
return new OperatorNode(node.op, node.fn, [
|
|
_derivative(arg1, constNodes),
|
|
_derivative(arg2, constNodes)
|
|
]);
|
|
case '*':
|
|
// d/dx(c*f(x)) = c*f'(x)
|
|
if (constNodes[arg1] !== undefined || constNodes[arg2] !== undefined) {
|
|
var newArgs = (constNodes[arg1] !== undefined)
|
|
? [arg1.clone(), _derivative(arg2, constNodes)]
|
|
: [arg2.clone(), _derivative(arg1, constNodes)];
|
|
|
|
return new OperatorNode('*', 'multiply', newArgs);
|
|
}
|
|
|
|
// Product Rule, d/dx(f(x)*g(x)) = f'(x)*g(x) + f(x)*g'(x)
|
|
return new OperatorNode('+', 'add', [
|
|
new OperatorNode('*', 'multiply', [_derivative(arg1, constNodes), arg2.clone()]),
|
|
new OperatorNode('*', 'multiply', [arg1.clone(), _derivative(arg2, constNodes)])
|
|
]);
|
|
case '/':
|
|
// d/dx(f(x) / c) = f'(x) / c
|
|
if (constNodes[arg2] !== undefined) {
|
|
return new OperatorNode('/', 'divide', [_derivative(arg1, constNodes), arg2]);
|
|
}
|
|
|
|
// Reciprocal Rule, d/dx(c / f(x)) = -c(f'(x)/f(x)^2)
|
|
if (constNodes[arg1] !== undefined) {
|
|
return new OperatorNode('*', 'multiply', [
|
|
new OperatorNode('-', 'unaryMinus', [arg1]),
|
|
new OperatorNode('/', 'divide', [
|
|
_derivative(arg2, constNodes),
|
|
new OperatorNode('^', 'pow', [arg2.clone(), new ConstantNode('2', config.number)])
|
|
])
|
|
]);
|
|
}
|
|
|
|
// Quotient rule, d/dx(f(x) / g(x)) = (f'(x)g(x) - f(x)g'(x)) / g(x)^2
|
|
return new OperatorNode('/', 'divide', [
|
|
new OperatorNode('-', 'subtract', [
|
|
new OperatorNode('*', 'multiply', [_derivative(arg1, constNodes), arg2.clone()]),
|
|
new OperatorNode('*', 'multiply', [arg1.clone(), _derivative(arg2, constNodes)])
|
|
]),
|
|
new OperatorNode('^', 'pow', [arg2.clone(), new ConstantNode('2', config.number)])
|
|
]);
|
|
case '^':
|
|
if (constNodes[arg1] !== undefined) {
|
|
// If is secretly constant; 0^f(x) = 1 (in JS), 1^f(x) = 1
|
|
if (type.isConstantNode(arg1) && (arg1.value === '0' || arg1.value === '1')) {
|
|
return new ConstantNode('0', config.number);
|
|
}
|
|
|
|
// d/dx(c^f(x)) = c^f(x)*ln(c)*f'(x)
|
|
return new OperatorNode('*', 'multiply', [
|
|
node,
|
|
new OperatorNode('*', 'multiply', [
|
|
new FunctionNode('log', [arg1.clone()]),
|
|
_derivative(arg2.clone(), constNodes)
|
|
])
|
|
]);
|
|
}
|
|
|
|
if (constNodes[arg2] !== undefined) {
|
|
if (type.isConstantNode(arg2)) {
|
|
var expValue = arg2.value;
|
|
|
|
// If is secretly constant; f(x)^0 = 1 -> d/dx(1) = 0
|
|
if (expValue === '0') {
|
|
return new ConstantNode('0', config.number);
|
|
}
|
|
// Ignore exponent; f(x)^1 = f(x)
|
|
if (expValue === '1') {
|
|
return _derivative(arg1, constNodes);
|
|
}
|
|
}
|
|
|
|
// Elementary Power Rule, d/dx(f(x)^c) = c*f'(x)*f(x)^(c-1)
|
|
var powMinusOne = new OperatorNode('^', 'pow', [
|
|
arg1.clone(),
|
|
new OperatorNode('-', 'subtract', [
|
|
arg2,
|
|
new ConstantNode('1', config.number)
|
|
])
|
|
]);
|
|
|
|
return new OperatorNode('*', 'multiply', [
|
|
arg2.clone(),
|
|
new OperatorNode('*', 'multiply', [
|
|
_derivative(arg1, constNodes),
|
|
powMinusOne
|
|
]),
|
|
]);
|
|
}
|
|
|
|
// Functional Power Rule, d/dx(f^g) = f^g*[f'*(g/f) + g'ln(f)]
|
|
return new OperatorNode('*', 'multiply', [
|
|
new OperatorNode('^', 'pow', [arg1.clone(), arg2.clone()]),
|
|
new OperatorNode('+', 'add', [
|
|
new OperatorNode('*', 'multiply', [
|
|
_derivative(arg1, constNodes),
|
|
new OperatorNode('/', 'divide', [arg2.clone(), arg1.clone()])
|
|
]),
|
|
new OperatorNode('*', 'multiply', [
|
|
_derivative(arg2, constNodes),
|
|
new FunctionNode('log', [arg1.clone()])
|
|
])
|
|
])
|
|
]);
|
|
case '%':
|
|
case 'mod':
|
|
default: throw new Error('Operator "' + node.op + '" not supported by derivative');
|
|
}
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Ensures the number of arguments for a function are correct,
|
|
* and will throw an error otherwise.
|
|
*
|
|
* @param {FunctionNode} node
|
|
*/
|
|
function funcArgsCheck(node) {
|
|
//TODO add min, max etc
|
|
if ((node.name == 'log' || node.name == 'nthRoot') && node.args.length == 2) {
|
|
return;
|
|
}
|
|
|
|
// There should be an incorrect number of arguments if we reach here
|
|
|
|
// Change all args to constants to avoid unidentified
|
|
// symbol error when compiling function
|
|
for (var i = 0; i < node.args.length; ++i) {
|
|
node.args[i] = new ConstantNode(0);
|
|
}
|
|
|
|
node.compile().eval();
|
|
throw new Error('Expected TypeError, but none found');
|
|
}
|
|
|
|
|
|
return derivative;
|
|
}
|
|
|
|
exports.name = 'derivative';
|
|
exports.factory = factory;
|