simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

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 });