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