derivative.js (24853B)
1 "use strict"; 2 3 Object.defineProperty(exports, "__esModule", { 4 value: true 5 }); 6 exports.createDerivative = void 0; 7 8 var _is = require("../../utils/is.js"); 9 10 var _factory = require("../../utils/factory.js"); 11 12 var name = 'derivative'; 13 var dependencies = ['typed', 'config', 'parse', 'simplify', 'equal', 'isZero', 'numeric', 'ConstantNode', 'FunctionNode', 'OperatorNode', 'ParenthesisNode', 'SymbolNode']; 14 var createDerivative = /* #__PURE__ */(0, _factory.factory)(name, dependencies, function (_ref) { 15 var typed = _ref.typed, 16 config = _ref.config, 17 parse = _ref.parse, 18 simplify = _ref.simplify, 19 equal = _ref.equal, 20 isZero = _ref.isZero, 21 numeric = _ref.numeric, 22 ConstantNode = _ref.ConstantNode, 23 FunctionNode = _ref.FunctionNode, 24 OperatorNode = _ref.OperatorNode, 25 ParenthesisNode = _ref.ParenthesisNode, 26 SymbolNode = _ref.SymbolNode; 27 28 /** 29 * Takes the derivative of an expression expressed in parser Nodes. 30 * The derivative will be taken over the supplied variable in the 31 * second parameter. If there are multiple variables in the expression, 32 * it will return a partial derivative. 33 * 34 * This uses rules of differentiation which can be found here: 35 * 36 * - [Differentiation rules (Wikipedia)](https://en.wikipedia.org/wiki/Differentiation_rules) 37 * 38 * Syntax: 39 * 40 * derivative(expr, variable) 41 * derivative(expr, variable, options) 42 * 43 * Examples: 44 * 45 * math.derivative('x^2', 'x') // Node {2 * x} 46 * math.derivative('x^2', 'x', {simplify: false}) // Node {2 * 1 * x ^ (2 - 1) 47 * math.derivative('sin(2x)', 'x')) // Node {2 * cos(2 * x)} 48 * math.derivative('2*x', 'x').evaluate() // number 2 49 * math.derivative('x^2', 'x').evaluate({x: 4}) // number 8 50 * const f = math.parse('x^2') 51 * const x = math.parse('x') 52 * math.derivative(f, x) // Node {2 * x} 53 * 54 * See also: 55 * 56 * simplify, parse, evaluate 57 * 58 * @param {Node | string} expr The expression to differentiate 59 * @param {SymbolNode | string} variable The variable over which to differentiate 60 * @param {{simplify: boolean}} [options] 61 * There is one option available, `simplify`, which 62 * is true by default. When false, output will not 63 * be simplified. 64 * @return {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} The derivative of `expr` 65 */ 66 var derivative = typed('derivative', { 67 'Node, SymbolNode, Object': function NodeSymbolNodeObject(expr, variable, options) { 68 var constNodes = {}; 69 constTag(constNodes, expr, variable.name); 70 71 var res = _derivative(expr, constNodes); 72 73 return options.simplify ? simplify(res) : res; 74 }, 75 'Node, SymbolNode': function NodeSymbolNode(expr, variable) { 76 return this(expr, variable, { 77 simplify: true 78 }); 79 }, 80 'string, SymbolNode': function stringSymbolNode(expr, variable) { 81 return this(parse(expr), variable); 82 }, 83 'string, SymbolNode, Object': function stringSymbolNodeObject(expr, variable, options) { 84 return this(parse(expr), variable, options); 85 }, 86 'string, string': function stringString(expr, variable) { 87 return this(parse(expr), parse(variable)); 88 }, 89 'string, string, Object': function stringStringObject(expr, variable, options) { 90 return this(parse(expr), parse(variable), options); 91 }, 92 'Node, string': function NodeString(expr, variable) { 93 return this(expr, parse(variable)); 94 }, 95 'Node, string, Object': function NodeStringObject(expr, variable, options) { 96 return this(expr, parse(variable), options); 97 } // TODO: replace the 8 signatures above with 4 as soon as typed-function supports optional arguments 98 99 /* TODO: implement and test syntax with order of derivatives -> implement as an option {order: number} 100 'Node, SymbolNode, ConstantNode': function (expr, variable, {order}) { 101 let res = expr 102 for (let i = 0; i < order; i++) { 103 let constNodes = {} 104 constTag(constNodes, expr, variable.name) 105 res = _derivative(res, constNodes) 106 } 107 return res 108 } 109 */ 110 111 }); 112 derivative._simplify = true; 113 114 derivative.toTex = function (deriv) { 115 return _derivTex.apply(null, deriv.args); 116 }; // FIXME: move the toTex method of derivative to latex.js. Difficulty is that it relies on parse. 117 // NOTE: the optional "order" parameter here is currently unused 118 119 120 var _derivTex = typed('_derivTex', { 121 'Node, SymbolNode': function NodeSymbolNode(expr, x) { 122 if ((0, _is.isConstantNode)(expr) && (0, _is.typeOf)(expr.value) === 'string') { 123 return _derivTex(parse(expr.value).toString(), x.toString(), 1); 124 } else { 125 return _derivTex(expr.toString(), x.toString(), 1); 126 } 127 }, 128 'Node, ConstantNode': function NodeConstantNode(expr, x) { 129 if ((0, _is.typeOf)(x.value) === 'string') { 130 return _derivTex(expr, parse(x.value)); 131 } else { 132 throw new Error("The second parameter to 'derivative' is a non-string constant"); 133 } 134 }, 135 'Node, SymbolNode, ConstantNode': function NodeSymbolNodeConstantNode(expr, x, order) { 136 return _derivTex(expr.toString(), x.name, order.value); 137 }, 138 'string, string, number': function stringStringNumber(expr, x, order) { 139 var d; 140 141 if (order === 1) { 142 d = '{d\\over d' + x + '}'; 143 } else { 144 d = '{d^{' + order + '}\\over d' + x + '^{' + order + '}}'; 145 } 146 147 return d + "\\left[".concat(expr, "\\right]"); 148 } 149 }); 150 /** 151 * Does a depth-first search on the expression tree to identify what Nodes 152 * are constants (e.g. 2 + 2), and stores the ones that are constants in 153 * constNodes. Classification is done as follows: 154 * 155 * 1. ConstantNodes are constants. 156 * 2. If there exists a SymbolNode, of which we are differentiating over, 157 * in the subtree it is not constant. 158 * 159 * @param {Object} constNodes Holds the nodes that are constant 160 * @param {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} node 161 * @param {string} varName Variable that we are differentiating 162 * @return {boolean} if node is constant 163 */ 164 // TODO: can we rewrite constTag into a pure function? 165 166 167 var constTag = typed('constTag', { 168 'Object, ConstantNode, string': function ObjectConstantNodeString(constNodes, node) { 169 constNodes[node] = true; 170 return true; 171 }, 172 'Object, SymbolNode, string': function ObjectSymbolNodeString(constNodes, node, varName) { 173 // Treat other variables like constants. For reasoning, see: 174 // https://en.wikipedia.org/wiki/Partial_derivative 175 if (node.name !== varName) { 176 constNodes[node] = true; 177 return true; 178 } 179 180 return false; 181 }, 182 'Object, ParenthesisNode, string': function ObjectParenthesisNodeString(constNodes, node, varName) { 183 return constTag(constNodes, node.content, varName); 184 }, 185 'Object, FunctionAssignmentNode, string': function ObjectFunctionAssignmentNodeString(constNodes, node, varName) { 186 if (node.params.indexOf(varName) === -1) { 187 constNodes[node] = true; 188 return true; 189 } 190 191 return constTag(constNodes, node.expr, varName); 192 }, 193 'Object, FunctionNode | OperatorNode, string': function ObjectFunctionNodeOperatorNodeString(constNodes, node, varName) { 194 if (node.args.length > 0) { 195 var isConst = constTag(constNodes, node.args[0], varName); 196 197 for (var i = 1; i < node.args.length; ++i) { 198 isConst = constTag(constNodes, node.args[i], varName) && isConst; 199 } 200 201 if (isConst) { 202 constNodes[node] = true; 203 return true; 204 } 205 } 206 207 return false; 208 } 209 }); 210 /** 211 * Applies differentiation rules. 212 * 213 * @param {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} node 214 * @param {Object} constNodes Holds the nodes that are constant 215 * @return {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} The derivative of `expr` 216 */ 217 218 var _derivative = typed('_derivative', { 219 'ConstantNode, Object': function ConstantNodeObject(node) { 220 return createConstantNode(0); 221 }, 222 'SymbolNode, Object': function SymbolNodeObject(node, constNodes) { 223 if (constNodes[node] !== undefined) { 224 return createConstantNode(0); 225 } 226 227 return createConstantNode(1); 228 }, 229 'ParenthesisNode, Object': function ParenthesisNodeObject(node, constNodes) { 230 return new ParenthesisNode(_derivative(node.content, constNodes)); 231 }, 232 'FunctionAssignmentNode, Object': function FunctionAssignmentNodeObject(node, constNodes) { 233 if (constNodes[node] !== undefined) { 234 return createConstantNode(0); 235 } 236 237 return _derivative(node.expr, constNodes); 238 }, 239 'FunctionNode, Object': function FunctionNodeObject(node, constNodes) { 240 if (node.args.length !== 1) { 241 funcArgsCheck(node); 242 } 243 244 if (constNodes[node] !== undefined) { 245 return createConstantNode(0); 246 } 247 248 var arg0 = node.args[0]; 249 var arg1; 250 var div = false; // is output a fraction? 251 252 var negative = false; // is output negative? 253 254 var funcDerivative; 255 256 switch (node.name) { 257 case 'cbrt': 258 // d/dx(cbrt(x)) = 1 / (3x^(2/3)) 259 div = true; 260 funcDerivative = new OperatorNode('*', 'multiply', [createConstantNode(3), new OperatorNode('^', 'pow', [arg0, new OperatorNode('/', 'divide', [createConstantNode(2), createConstantNode(3)])])]); 261 break; 262 263 case 'sqrt': 264 case 'nthRoot': 265 // d/dx(sqrt(x)) = 1 / (2*sqrt(x)) 266 if (node.args.length === 1) { 267 div = true; 268 funcDerivative = new OperatorNode('*', 'multiply', [createConstantNode(2), new FunctionNode('sqrt', [arg0])]); 269 } else if (node.args.length === 2) { 270 // Rearrange from nthRoot(x, a) -> x^(1/a) 271 arg1 = new OperatorNode('/', 'divide', [createConstantNode(1), node.args[1]]); // Is a variable? 272 273 constNodes[arg1] = constNodes[node.args[1]]; 274 return _derivative(new OperatorNode('^', 'pow', [arg0, arg1]), constNodes); 275 } 276 277 break; 278 279 case 'log10': 280 arg1 = createConstantNode(10); 281 282 /* fall through! */ 283 284 case 'log': 285 if (!arg1 && node.args.length === 1) { 286 // d/dx(log(x)) = 1 / x 287 funcDerivative = arg0.clone(); 288 div = true; 289 } else if (node.args.length === 1 && arg1 || node.args.length === 2 && constNodes[node.args[1]] !== undefined) { 290 // d/dx(log(x, c)) = 1 / (x*ln(c)) 291 funcDerivative = new OperatorNode('*', 'multiply', [arg0.clone(), new FunctionNode('log', [arg1 || node.args[1]])]); 292 div = true; 293 } else if (node.args.length === 2) { 294 // d/dx(log(f(x), g(x))) = d/dx(log(f(x)) / log(g(x))) 295 return _derivative(new OperatorNode('/', 'divide', [new FunctionNode('log', [arg0]), new FunctionNode('log', [node.args[1]])]), constNodes); 296 } 297 298 break; 299 300 case 'pow': 301 constNodes[arg1] = constNodes[node.args[1]]; // Pass to pow operator node parser 302 303 return _derivative(new OperatorNode('^', 'pow', [arg0, node.args[1]]), constNodes); 304 305 case 'exp': 306 // d/dx(e^x) = e^x 307 funcDerivative = new FunctionNode('exp', [arg0.clone()]); 308 break; 309 310 case 'sin': 311 // d/dx(sin(x)) = cos(x) 312 funcDerivative = new FunctionNode('cos', [arg0.clone()]); 313 break; 314 315 case 'cos': 316 // d/dx(cos(x)) = -sin(x) 317 funcDerivative = new OperatorNode('-', 'unaryMinus', [new FunctionNode('sin', [arg0.clone()])]); 318 break; 319 320 case 'tan': 321 // d/dx(tan(x)) = sec(x)^2 322 funcDerivative = new OperatorNode('^', 'pow', [new FunctionNode('sec', [arg0.clone()]), createConstantNode(2)]); 323 break; 324 325 case 'sec': 326 // d/dx(sec(x)) = sec(x)tan(x) 327 funcDerivative = new OperatorNode('*', 'multiply', [node, new FunctionNode('tan', [arg0.clone()])]); 328 break; 329 330 case 'csc': 331 // d/dx(csc(x)) = -csc(x)cot(x) 332 negative = true; 333 funcDerivative = new OperatorNode('*', 'multiply', [node, new FunctionNode('cot', [arg0.clone()])]); 334 break; 335 336 case 'cot': 337 // d/dx(cot(x)) = -csc(x)^2 338 negative = true; 339 funcDerivative = new OperatorNode('^', 'pow', [new FunctionNode('csc', [arg0.clone()]), createConstantNode(2)]); 340 break; 341 342 case 'asin': 343 // d/dx(asin(x)) = 1 / sqrt(1 - x^2) 344 div = true; 345 funcDerivative = new FunctionNode('sqrt', [new OperatorNode('-', 'subtract', [createConstantNode(1), new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)])])]); 346 break; 347 348 case 'acos': 349 // d/dx(acos(x)) = -1 / sqrt(1 - x^2) 350 div = true; 351 negative = true; 352 funcDerivative = new FunctionNode('sqrt', [new OperatorNode('-', 'subtract', [createConstantNode(1), new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)])])]); 353 break; 354 355 case 'atan': 356 // d/dx(atan(x)) = 1 / (x^2 + 1) 357 div = true; 358 funcDerivative = new OperatorNode('+', 'add', [new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)]), createConstantNode(1)]); 359 break; 360 361 case 'asec': 362 // d/dx(asec(x)) = 1 / (|x|*sqrt(x^2 - 1)) 363 div = true; 364 funcDerivative = new OperatorNode('*', 'multiply', [new FunctionNode('abs', [arg0.clone()]), new FunctionNode('sqrt', [new OperatorNode('-', 'subtract', [new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)]), createConstantNode(1)])])]); 365 break; 366 367 case 'acsc': 368 // d/dx(acsc(x)) = -1 / (|x|*sqrt(x^2 - 1)) 369 div = true; 370 negative = true; 371 funcDerivative = new OperatorNode('*', 'multiply', [new FunctionNode('abs', [arg0.clone()]), new FunctionNode('sqrt', [new OperatorNode('-', 'subtract', [new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)]), createConstantNode(1)])])]); 372 break; 373 374 case 'acot': 375 // d/dx(acot(x)) = -1 / (x^2 + 1) 376 div = true; 377 negative = true; 378 funcDerivative = new OperatorNode('+', 'add', [new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)]), createConstantNode(1)]); 379 break; 380 381 case 'sinh': 382 // d/dx(sinh(x)) = cosh(x) 383 funcDerivative = new FunctionNode('cosh', [arg0.clone()]); 384 break; 385 386 case 'cosh': 387 // d/dx(cosh(x)) = sinh(x) 388 funcDerivative = new FunctionNode('sinh', [arg0.clone()]); 389 break; 390 391 case 'tanh': 392 // d/dx(tanh(x)) = sech(x)^2 393 funcDerivative = new OperatorNode('^', 'pow', [new FunctionNode('sech', [arg0.clone()]), createConstantNode(2)]); 394 break; 395 396 case 'sech': 397 // d/dx(sech(x)) = -sech(x)tanh(x) 398 negative = true; 399 funcDerivative = new OperatorNode('*', 'multiply', [node, new FunctionNode('tanh', [arg0.clone()])]); 400 break; 401 402 case 'csch': 403 // d/dx(csch(x)) = -csch(x)coth(x) 404 negative = true; 405 funcDerivative = new OperatorNode('*', 'multiply', [node, new FunctionNode('coth', [arg0.clone()])]); 406 break; 407 408 case 'coth': 409 // d/dx(coth(x)) = -csch(x)^2 410 negative = true; 411 funcDerivative = new OperatorNode('^', 'pow', [new FunctionNode('csch', [arg0.clone()]), createConstantNode(2)]); 412 break; 413 414 case 'asinh': 415 // d/dx(asinh(x)) = 1 / sqrt(x^2 + 1) 416 div = true; 417 funcDerivative = new FunctionNode('sqrt', [new OperatorNode('+', 'add', [new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)]), createConstantNode(1)])]); 418 break; 419 420 case 'acosh': 421 // d/dx(acosh(x)) = 1 / sqrt(x^2 - 1); XXX potentially only for x >= 1 (the real spectrum) 422 div = true; 423 funcDerivative = new FunctionNode('sqrt', [new OperatorNode('-', 'subtract', [new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)]), createConstantNode(1)])]); 424 break; 425 426 case 'atanh': 427 // d/dx(atanh(x)) = 1 / (1 - x^2) 428 div = true; 429 funcDerivative = new OperatorNode('-', 'subtract', [createConstantNode(1), new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)])]); 430 break; 431 432 case 'asech': 433 // d/dx(asech(x)) = -1 / (x*sqrt(1 - x^2)) 434 div = true; 435 negative = true; 436 funcDerivative = new OperatorNode('*', 'multiply', [arg0.clone(), new FunctionNode('sqrt', [new OperatorNode('-', 'subtract', [createConstantNode(1), new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)])])])]); 437 break; 438 439 case 'acsch': 440 // d/dx(acsch(x)) = -1 / (|x|*sqrt(x^2 + 1)) 441 div = true; 442 negative = true; 443 funcDerivative = new OperatorNode('*', 'multiply', [new FunctionNode('abs', [arg0.clone()]), new FunctionNode('sqrt', [new OperatorNode('+', 'add', [new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)]), createConstantNode(1)])])]); 444 break; 445 446 case 'acoth': 447 // d/dx(acoth(x)) = -1 / (1 - x^2) 448 div = true; 449 negative = true; 450 funcDerivative = new OperatorNode('-', 'subtract', [createConstantNode(1), new OperatorNode('^', 'pow', [arg0.clone(), createConstantNode(2)])]); 451 break; 452 453 case 'abs': 454 // d/dx(abs(x)) = abs(x)/x 455 funcDerivative = new OperatorNode('/', 'divide', [new FunctionNode(new SymbolNode('abs'), [arg0.clone()]), arg0.clone()]); 456 break; 457 458 case 'gamma': // Needs digamma function, d/dx(gamma(x)) = gamma(x)digamma(x) 459 460 default: 461 throw new Error('Function "' + node.name + '" is not supported by derivative, or a wrong number of arguments is passed'); 462 } 463 464 var op, func; 465 466 if (div) { 467 op = '/'; 468 func = 'divide'; 469 } else { 470 op = '*'; 471 func = 'multiply'; 472 } 473 /* Apply chain rule to all functions: 474 F(x) = f(g(x)) 475 F'(x) = g'(x)*f'(g(x)) */ 476 477 478 var chainDerivative = _derivative(arg0, constNodes); 479 480 if (negative) { 481 chainDerivative = new OperatorNode('-', 'unaryMinus', [chainDerivative]); 482 } 483 484 return new OperatorNode(op, func, [chainDerivative, funcDerivative]); 485 }, 486 'OperatorNode, Object': function OperatorNodeObject(node, constNodes) { 487 if (constNodes[node] !== undefined) { 488 return createConstantNode(0); 489 } 490 491 if (node.op === '+') { 492 // d/dx(sum(f(x)) = sum(f'(x)) 493 return new OperatorNode(node.op, node.fn, node.args.map(function (arg) { 494 return _derivative(arg, constNodes); 495 })); 496 } 497 498 if (node.op === '-') { 499 // d/dx(+/-f(x)) = +/-f'(x) 500 if (node.isUnary()) { 501 return new OperatorNode(node.op, node.fn, [_derivative(node.args[0], constNodes)]); 502 } // Linearity of differentiation, d/dx(f(x) +/- g(x)) = f'(x) +/- g'(x) 503 504 505 if (node.isBinary()) { 506 return new OperatorNode(node.op, node.fn, [_derivative(node.args[0], constNodes), _derivative(node.args[1], constNodes)]); 507 } 508 } 509 510 if (node.op === '*') { 511 // d/dx(c*f(x)) = c*f'(x) 512 var constantTerms = node.args.filter(function (arg) { 513 return constNodes[arg] !== undefined; 514 }); 515 516 if (constantTerms.length > 0) { 517 var nonConstantTerms = node.args.filter(function (arg) { 518 return constNodes[arg] === undefined; 519 }); 520 var nonConstantNode = nonConstantTerms.length === 1 ? nonConstantTerms[0] : new OperatorNode('*', 'multiply', nonConstantTerms); 521 var newArgs = constantTerms.concat(_derivative(nonConstantNode, constNodes)); 522 return new OperatorNode('*', 'multiply', newArgs); 523 } // Product Rule, d/dx(f(x)*g(x)) = f'(x)*g(x) + f(x)*g'(x) 524 525 526 return new OperatorNode('+', 'add', node.args.map(function (argOuter) { 527 return new OperatorNode('*', 'multiply', node.args.map(function (argInner) { 528 return argInner === argOuter ? _derivative(argInner, constNodes) : argInner.clone(); 529 })); 530 })); 531 } 532 533 if (node.op === '/' && node.isBinary()) { 534 var arg0 = node.args[0]; 535 var arg1 = node.args[1]; // d/dx(f(x) / c) = f'(x) / c 536 537 if (constNodes[arg1] !== undefined) { 538 return new OperatorNode('/', 'divide', [_derivative(arg0, constNodes), arg1]); 539 } // Reciprocal Rule, d/dx(c / f(x)) = -c(f'(x)/f(x)^2) 540 541 542 if (constNodes[arg0] !== undefined) { 543 return new OperatorNode('*', 'multiply', [new OperatorNode('-', 'unaryMinus', [arg0]), new OperatorNode('/', 'divide', [_derivative(arg1, constNodes), new OperatorNode('^', 'pow', [arg1.clone(), createConstantNode(2)])])]); 544 } // Quotient rule, d/dx(f(x) / g(x)) = (f'(x)g(x) - f(x)g'(x)) / g(x)^2 545 546 547 return new OperatorNode('/', 'divide', [new OperatorNode('-', 'subtract', [new OperatorNode('*', 'multiply', [_derivative(arg0, constNodes), arg1.clone()]), new OperatorNode('*', 'multiply', [arg0.clone(), _derivative(arg1, constNodes)])]), new OperatorNode('^', 'pow', [arg1.clone(), createConstantNode(2)])]); 548 } 549 550 if (node.op === '^' && node.isBinary()) { 551 var _arg = node.args[0]; 552 var _arg2 = node.args[1]; 553 554 if (constNodes[_arg] !== undefined) { 555 // If is secretly constant; 0^f(x) = 1 (in JS), 1^f(x) = 1 556 if ((0, _is.isConstantNode)(_arg) && (isZero(_arg.value) || equal(_arg.value, 1))) { 557 return createConstantNode(0); 558 } // d/dx(c^f(x)) = c^f(x)*ln(c)*f'(x) 559 560 561 return new OperatorNode('*', 'multiply', [node, new OperatorNode('*', 'multiply', [new FunctionNode('log', [_arg.clone()]), _derivative(_arg2.clone(), constNodes)])]); 562 } 563 564 if (constNodes[_arg2] !== undefined) { 565 if ((0, _is.isConstantNode)(_arg2)) { 566 // If is secretly constant; f(x)^0 = 1 -> d/dx(1) = 0 567 if (isZero(_arg2.value)) { 568 return createConstantNode(0); 569 } // Ignore exponent; f(x)^1 = f(x) 570 571 572 if (equal(_arg2.value, 1)) { 573 return _derivative(_arg, constNodes); 574 } 575 } // Elementary Power Rule, d/dx(f(x)^c) = c*f'(x)*f(x)^(c-1) 576 577 578 var powMinusOne = new OperatorNode('^', 'pow', [_arg.clone(), new OperatorNode('-', 'subtract', [_arg2, createConstantNode(1)])]); 579 return new OperatorNode('*', 'multiply', [_arg2.clone(), new OperatorNode('*', 'multiply', [_derivative(_arg, constNodes), powMinusOne])]); 580 } // Functional Power Rule, d/dx(f^g) = f^g*[f'*(g/f) + g'ln(f)] 581 582 583 return new OperatorNode('*', 'multiply', [new OperatorNode('^', 'pow', [_arg.clone(), _arg2.clone()]), new OperatorNode('+', 'add', [new OperatorNode('*', 'multiply', [_derivative(_arg, constNodes), new OperatorNode('/', 'divide', [_arg2.clone(), _arg.clone()])]), new OperatorNode('*', 'multiply', [_derivative(_arg2, constNodes), new FunctionNode('log', [_arg.clone()])])])]); 584 } 585 586 throw new Error('Operator "' + node.op + '" is not supported by derivative, or a wrong number of arguments is passed'); 587 } 588 }); 589 /** 590 * Ensures the number of arguments for a function are correct, 591 * and will throw an error otherwise. 592 * 593 * @param {FunctionNode} node 594 */ 595 596 597 function funcArgsCheck(node) { 598 // TODO add min, max etc 599 if ((node.name === 'log' || node.name === 'nthRoot' || node.name === 'pow') && node.args.length === 2) { 600 return; 601 } // There should be an incorrect number of arguments if we reach here 602 // Change all args to constants to avoid unidentified 603 // symbol error when compiling function 604 605 606 for (var i = 0; i < node.args.length; ++i) { 607 node.args[i] = createConstantNode(0); 608 } 609 610 node.compile().evaluate(); 611 throw new Error('Expected TypeError, but none found'); 612 } 613 /** 614 * Helper function to create a constant node with a specific type 615 * (number, BigNumber, Fraction) 616 * @param {number} value 617 * @param {string} [valueType] 618 * @return {ConstantNode} 619 */ 620 621 622 function createConstantNode(value, valueType) { 623 return new ConstantNode(numeric(value, valueType || config.number)); 624 } 625 626 return derivative; 627 }); 628 exports.createDerivative = createDerivative;