UNPKG

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