jiangchengfeiyi-xiaochengxu/node_modules/mathjs/lib/cjs/function/algebra/derivative.js
2025-01-02 11:13:50 +08:00

512 lines
22 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createDerivative = void 0;
var _is = require("../../utils/is.js");
var _factory = require("../../utils/factory.js");
var _number = require("../../utils/number.js");
const name = 'derivative';
const dependencies = ['typed', 'config', 'parse', 'simplify', 'equal', 'isZero', 'numeric', 'ConstantNode', 'FunctionNode', 'OperatorNode', 'ParenthesisNode', 'SymbolNode'];
const createDerivative = exports.createDerivative = /* #__PURE__ */(0, _factory.factory)(name, dependencies, _ref => {
let {
typed,
config,
parse,
simplify,
equal,
isZero,
numeric,
ConstantNode,
FunctionNode,
OperatorNode,
ParenthesisNode,
SymbolNode
} = _ref;
/**
* 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)](https://en.wikipedia.org/wiki/Differentiation_rules)
*
* Syntax:
*
* math.derivative(expr, variable)
* math.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').evaluate() // number 2
* math.derivative('x^2', 'x').evaluate({x: 4}) // number 8
* const f = math.parse('x^2')
* const x = math.parse('x')
* math.derivative(f, x) // Node {2 * x}
*
* See also:
*
* simplify, parse, evaluate
*
* @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`
*/
function plainDerivative(expr, variable) {
let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {
simplify: true
};
const cache = new Map();
const variableName = variable.name;
function isConstCached(node) {
const cached = cache.get(node);
if (cached !== undefined) {
return cached;
}
const res = _isConst(isConstCached, node, variableName);
cache.set(node, res);
return res;
}
const res = _derivative(expr, isConstCached);
return options.simplify ? simplify(res) : res;
}
function parseIdentifier(string) {
const symbol = parse(string);
if (!symbol.isSymbolNode) {
throw new TypeError('Invalid variable. ' + `Cannot parse ${JSON.stringify(string)} into a variable in function derivative`);
}
return symbol;
}
const derivative = typed(name, {
'Node, SymbolNode': plainDerivative,
'Node, SymbolNode, Object': plainDerivative,
'Node, string': (node, symbol) => plainDerivative(node, parseIdentifier(symbol)),
'Node, string, Object': (node, symbol, options) => plainDerivative(node, parseIdentifier(symbol), options)
/* TODO: implement and test syntax with order of derivatives -> implement as an option {order: number}
'Node, SymbolNode, ConstantNode': function (expr, variable, {order}) {
let res = expr
for (let i = 0; i < order; i++) {
<create caching isConst>
res = _derivative(res, isConst)
}
return res
}
*/
});
derivative._simplify = true;
derivative.toTex = function (deriv) {
return _derivTex.apply(null, deriv.args);
};
// FIXME: move the toTex method of derivative to latex.js. Difficulty is that it relies on parse.
// NOTE: the optional "order" parameter here is currently unused
const _derivTex = typed('_derivTex', {
'Node, SymbolNode': function (expr, x) {
if ((0, _is.isConstantNode)(expr) && (0, _is.typeOf)(expr.value) === 'string') {
return _derivTex(parse(expr.value).toString(), x.toString(), 1);
} else {
return _derivTex(expr.toTex(), x.toString(), 1);
}
},
'Node, ConstantNode': function (expr, x) {
if ((0, _is.typeOf)(x.value) === 'string') {
return _derivTex(expr, parse(x.value));
} else {
throw new Error("The second parameter to 'derivative' is a non-string constant");
}
},
'Node, SymbolNode, ConstantNode': function (expr, x, order) {
return _derivTex(expr.toString(), x.name, order.value);
},
'string, string, number': function (expr, x, order) {
let d;
if (order === 1) {
d = '{d\\over d' + x + '}';
} else {
d = '{d^{' + order + '}\\over d' + x + '^{' + order + '}}';
}
return d + `\\left[${expr}\\right]`;
}
});
/**
* Checks if a node is constants (e.g. 2 + 2).
* Accepts (usually memoized) version of self as the first parameter for recursive calls.
* 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 {function} isConst Function that tells whether sub-expression is a constant
* @param {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} node
* @param {string} varName Variable that we are differentiating
* @return {boolean} if node is constant
*/
const _isConst = typed('_isConst', {
'function, ConstantNode, string': function () {
return true;
},
'function, SymbolNode, string': function (isConst, node, varName) {
// Treat other variables like constants. For reasoning, see:
// https://en.wikipedia.org/wiki/Partial_derivative
return node.name !== varName;
},
'function, ParenthesisNode, string': function (isConst, node, varName) {
return isConst(node.content, varName);
},
'function, FunctionAssignmentNode, string': function (isConst, node, varName) {
if (!node.params.includes(varName)) {
return true;
}
return isConst(node.expr, varName);
},
'function, FunctionNode | OperatorNode, string': function (isConst, node, varName) {
return node.args.every(arg => isConst(arg, varName));
}
});
/**
* Applies differentiation rules.
*
* @param {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} node
* @param {function} isConst Function that tells if a node is constant
* @return {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} The derivative of `expr`
*/
const _derivative = typed('_derivative', {
'ConstantNode, function': function () {
return createConstantNode(0);
},
'SymbolNode, function': function (node, isConst) {
if (isConst(node)) {
return createConstantNode(0);
}
return createConstantNode(1);
},
'ParenthesisNode, function': function (node, isConst) {
return new ParenthesisNode(_derivative(node.content, isConst));
},
'FunctionAssignmentNode, function': function (node, isConst) {
if (isConst(node)) {
return createConstantNode(0);
}
return _derivative(node.expr, isConst);
},
'FunctionNode, function': function (node, isConst) {
if (isConst(node)) {
return createConstantNode(0);
}
const arg0 = node.args[0];
let arg1;
let div = false; // is output a fraction?
let negative = false; // is output negative?
let funcDerivative;
switch (node.name) {
case 'cbrt':
// d/dx(cbrt(x)) = 1 / (3x^(2/3))
div = true;
funcDerivative = new OperatorNode('*', 'multiply', [createConstantNode(3), new OperatorNode('^', 'pow', [arg0, new OperatorNode('/', 'divide', [createConstantNode(2), createConstantNode(3)])])]);
break;
case 'sqrt':
case 'nthRoot':
// d/dx(sqrt(x)) = 1 / (2*sqrt(x))
if (node.args.length === 1) {
div = true;
funcDerivative = new OperatorNode('*', 'multiply', [createConstantNode(2), new FunctionNode('sqrt', [arg0])]);
} else if (node.args.length === 2) {
// Rearrange from nthRoot(x, a) -> x^(1/a)
arg1 = new OperatorNode('/', 'divide', [createConstantNode(1), node.args[1]]);
return _derivative(new OperatorNode('^', 'pow', [arg0, arg1]), isConst);
}
break;
case 'log10':
arg1 = createConstantNode(10);
/* fall through! */
case 'log':
if (!arg1 && node.args.length === 1) {
// d/dx(log(x)) = 1 / x
funcDerivative = arg0.clone();
div = true;
} else if (node.args.length === 1 && arg1 || node.args.length === 2 && isConst(node.args[1])) {
// d/dx(log(x, c)) = 1 / (x*ln(c))
funcDerivative = new OperatorNode('*', 'multiply', [arg0.clone(), new FunctionNode('log', [arg1 || node.args[1]])]);
div = true;
} else if (node.args.length === 2) {
// d/dx(log(f(x), g(x))) = d/dx(log(f(x)) / log(g(x)))
return _derivative(new OperatorNode('/', 'divide', [new FunctionNode('log', [arg0]), new FunctionNode('log', [node.args[1]])]), isConst);
}
break;
case 'pow':
if (node.args.length === 2) {
// Pass to pow operator node parser
return _derivative(new OperatorNode('^', 'pow', [arg0, node.args[1]]), isConst);
}
break;
case 'exp':
// d/dx(e^x) = e^x
funcDerivative = new FunctionNode('exp', [arg0.clone()]);
break;
case 'sin':
// d/dx(sin(x)) = cos(x)
funcDerivative = new FunctionNode('cos', [arg0.clone()]);
break;
case 'cos':
// d/dx(cos(x)) = -sin(x)
funcDerivative = new OperatorNode('-', 'unaryMinus', [new FunctionNode('sin', [arg0.clone()])]);
break;
case 'tan':
// d/dx(tan(x)) = sec(x)^2
funcDerivative = new OperatorNode('^', 'pow', [new FunctionNode('sec', [arg0.clone()]), createConstantNode(2)]);
break;
case 'sec':
// d/dx(sec(x)) = sec(x)tan(x)
funcDerivative = new OperatorNode('*', 'multiply', [node, new FunctionNode('tan', [arg0.clone()])]);
break;
case 'csc':
// d/dx(csc(x)) = -csc(x)cot(x)
negative = true;
funcDerivative = new OperatorNode('*', 'multiply', [node, new FunctionNode('cot', [arg0.clone()])]);
break;
case 'cot':
// d/dx(cot(x)) = -csc(x)^2
negative = true;
funcDerivative = new OperatorNode('^', 'pow', [new FunctionNode('csc', [arg0.clone()]), createConstantNode(2)]);
break;
case 'asin':
// d/dx(asin(x)) = 1 / sqrt(1 - x^2)
div = true;
funcDerivative = new FunctionNode('sqrt', [new OperatorNode('-', 'subtract', [createConstantNode(1), new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)])])]);
break;
case 'acos':
// d/dx(acos(x)) = -1 / sqrt(1 - x^2)
div = true;
negative = true;
funcDerivative = new FunctionNode('sqrt', [new OperatorNode('-', 'subtract', [createConstantNode(1), new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)])])]);
break;
case 'atan':
// d/dx(atan(x)) = 1 / (x^2 + 1)
div = true;
funcDerivative = new OperatorNode('+', 'add', [new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)]), createConstantNode(1)]);
break;
case 'asec':
// d/dx(asec(x)) = 1 / (|x|*sqrt(x^2 - 1))
div = true;
funcDerivative = new OperatorNode('*', 'multiply', [new FunctionNode('abs', [arg0.clone()]), new FunctionNode('sqrt', [new OperatorNode('-', 'subtract', [new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)]), createConstantNode(1)])])]);
break;
case 'acsc':
// d/dx(acsc(x)) = -1 / (|x|*sqrt(x^2 - 1))
div = true;
negative = true;
funcDerivative = new OperatorNode('*', 'multiply', [new FunctionNode('abs', [arg0.clone()]), new FunctionNode('sqrt', [new OperatorNode('-', 'subtract', [new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)]), createConstantNode(1)])])]);
break;
case 'acot':
// d/dx(acot(x)) = -1 / (x^2 + 1)
div = true;
negative = true;
funcDerivative = new OperatorNode('+', 'add', [new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)]), createConstantNode(1)]);
break;
case 'sinh':
// d/dx(sinh(x)) = cosh(x)
funcDerivative = new FunctionNode('cosh', [arg0.clone()]);
break;
case 'cosh':
// d/dx(cosh(x)) = sinh(x)
funcDerivative = new FunctionNode('sinh', [arg0.clone()]);
break;
case 'tanh':
// d/dx(tanh(x)) = sech(x)^2
funcDerivative = new OperatorNode('^', 'pow', [new FunctionNode('sech', [arg0.clone()]), createConstantNode(2)]);
break;
case 'sech':
// d/dx(sech(x)) = -sech(x)tanh(x)
negative = true;
funcDerivative = new OperatorNode('*', 'multiply', [node, new FunctionNode('tanh', [arg0.clone()])]);
break;
case 'csch':
// d/dx(csch(x)) = -csch(x)coth(x)
negative = true;
funcDerivative = new OperatorNode('*', 'multiply', [node, new FunctionNode('coth', [arg0.clone()])]);
break;
case 'coth':
// d/dx(coth(x)) = -csch(x)^2
negative = true;
funcDerivative = new OperatorNode('^', 'pow', [new FunctionNode('csch', [arg0.clone()]), createConstantNode(2)]);
break;
case 'asinh':
// d/dx(asinh(x)) = 1 / sqrt(x^2 + 1)
div = true;
funcDerivative = new FunctionNode('sqrt', [new OperatorNode('+', 'add', [new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)]), createConstantNode(1)])]);
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', [arg0.clone(), createConstantNode(2)]), createConstantNode(1)])]);
break;
case 'atanh':
// d/dx(atanh(x)) = 1 / (1 - x^2)
div = true;
funcDerivative = new OperatorNode('-', 'subtract', [createConstantNode(1), new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)])]);
break;
case 'asech':
// d/dx(asech(x)) = -1 / (x*sqrt(1 - x^2))
div = true;
negative = true;
funcDerivative = new OperatorNode('*', 'multiply', [arg0.clone(), new FunctionNode('sqrt', [new OperatorNode('-', 'subtract', [createConstantNode(1), new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)])])])]);
break;
case 'acsch':
// d/dx(acsch(x)) = -1 / (|x|*sqrt(x^2 + 1))
div = true;
negative = true;
funcDerivative = new OperatorNode('*', 'multiply', [new FunctionNode('abs', [arg0.clone()]), new FunctionNode('sqrt', [new OperatorNode('+', 'add', [new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)]), createConstantNode(1)])])]);
break;
case 'acoth':
// d/dx(acoth(x)) = -1 / (1 - x^2)
div = true;
negative = true;
funcDerivative = new OperatorNode('-', 'subtract', [createConstantNode(1), new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)])]);
break;
case 'abs':
// d/dx(abs(x)) = abs(x)/x
funcDerivative = new OperatorNode('/', 'divide', [new FunctionNode(new SymbolNode('abs'), [arg0.clone()]), arg0.clone()]);
break;
case 'gamma': // Needs digamma function, d/dx(gamma(x)) = gamma(x)digamma(x)
default:
throw new Error('Cannot process function "' + node.name + '" in derivative: ' + 'the function is not supported, undefined, or the number of arguments passed to it are not supported');
}
let 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)) */
let chainDerivative = _derivative(arg0, isConst);
if (negative) {
chainDerivative = new OperatorNode('-', 'unaryMinus', [chainDerivative]);
}
return new OperatorNode(op, func, [chainDerivative, funcDerivative]);
},
'OperatorNode, function': function (node, isConst) {
if (isConst(node)) {
return createConstantNode(0);
}
if (node.op === '+') {
// d/dx(sum(f(x)) = sum(f'(x))
return new OperatorNode(node.op, node.fn, node.args.map(function (arg) {
return _derivative(arg, isConst);
}));
}
if (node.op === '-') {
// d/dx(+/-f(x)) = +/-f'(x)
if (node.isUnary()) {
return new OperatorNode(node.op, node.fn, [_derivative(node.args[0], isConst)]);
}
// Linearity of differentiation, d/dx(f(x) +/- g(x)) = f'(x) +/- g'(x)
if (node.isBinary()) {
return new OperatorNode(node.op, node.fn, [_derivative(node.args[0], isConst), _derivative(node.args[1], isConst)]);
}
}
if (node.op === '*') {
// d/dx(c*f(x)) = c*f'(x)
const constantTerms = node.args.filter(function (arg) {
return isConst(arg);
});
if (constantTerms.length > 0) {
const nonConstantTerms = node.args.filter(function (arg) {
return !isConst(arg);
});
const nonConstantNode = nonConstantTerms.length === 1 ? nonConstantTerms[0] : new OperatorNode('*', 'multiply', nonConstantTerms);
const newArgs = constantTerms.concat(_derivative(nonConstantNode, isConst));
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', node.args.map(function (argOuter) {
return new OperatorNode('*', 'multiply', node.args.map(function (argInner) {
return argInner === argOuter ? _derivative(argInner, isConst) : argInner.clone();
}));
}));
}
if (node.op === '/' && node.isBinary()) {
const arg0 = node.args[0];
const arg1 = node.args[1];
// d/dx(f(x) / c) = f'(x) / c
if (isConst(arg1)) {
return new OperatorNode('/', 'divide', [_derivative(arg0, isConst), arg1]);
}
// Reciprocal Rule, d/dx(c / f(x)) = -c(f'(x)/f(x)^2)
if (isConst(arg0)) {
return new OperatorNode('*', 'multiply', [new OperatorNode('-', 'unaryMinus', [arg0]), new OperatorNode('/', 'divide', [_derivative(arg1, isConst), new OperatorNode('^', 'pow', [arg1.clone(), createConstantNode(2)])])]);
}
// 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(arg0, isConst), arg1.clone()]), new OperatorNode('*', 'multiply', [arg0.clone(), _derivative(arg1, isConst)])]), new OperatorNode('^', 'pow', [arg1.clone(), createConstantNode(2)])]);
}
if (node.op === '^' && node.isBinary()) {
const arg0 = node.args[0];
const arg1 = node.args[1];
if (isConst(arg0)) {
// If is secretly constant; 0^f(x) = 1 (in JS), 1^f(x) = 1
if ((0, _is.isConstantNode)(arg0) && (isZero(arg0.value) || equal(arg0.value, 1))) {
return createConstantNode(0);
}
// d/dx(c^f(x)) = c^f(x)*ln(c)*f'(x)
return new OperatorNode('*', 'multiply', [node, new OperatorNode('*', 'multiply', [new FunctionNode('log', [arg0.clone()]), _derivative(arg1.clone(), isConst)])]);
}
if (isConst(arg1)) {
if ((0, _is.isConstantNode)(arg1)) {
// If is secretly constant; f(x)^0 = 1 -> d/dx(1) = 0
if (isZero(arg1.value)) {
return createConstantNode(0);
}
// Ignore exponent; f(x)^1 = f(x)
if (equal(arg1.value, 1)) {
return _derivative(arg0, isConst);
}
}
// Elementary Power Rule, d/dx(f(x)^c) = c*f'(x)*f(x)^(c-1)
const powMinusOne = new OperatorNode('^', 'pow', [arg0.clone(), new OperatorNode('-', 'subtract', [arg1, createConstantNode(1)])]);
return new OperatorNode('*', 'multiply', [arg1.clone(), new OperatorNode('*', 'multiply', [_derivative(arg0, isConst), powMinusOne])]);
}
// Functional Power Rule, d/dx(f^g) = f^g*[f'*(g/f) + g'ln(f)]
return new OperatorNode('*', 'multiply', [new OperatorNode('^', 'pow', [arg0.clone(), arg1.clone()]), new OperatorNode('+', 'add', [new OperatorNode('*', 'multiply', [_derivative(arg0, isConst), new OperatorNode('/', 'divide', [arg1.clone(), arg0.clone()])]), new OperatorNode('*', 'multiply', [_derivative(arg1, isConst), new FunctionNode('log', [arg0.clone()])])])]);
}
throw new Error('Cannot process operator "' + node.op + '" in derivative: ' + 'the operator is not supported, undefined, or the number of arguments passed to it are not supported');
}
});
/**
* Helper function to create a constant node with a specific type
* (number, BigNumber, Fraction)
* @param {number} value
* @param {string} [valueType]
* @return {ConstantNode}
*/
function createConstantNode(value, valueType) {
return new ConstantNode(numeric(value, valueType || (0, _number.safeNumberType)(String(value), config)));
}
return derivative;
});