function plainDerivative (expr, variable, options = { simplify: true }) {
    const cache = new Map()
    const variableName = variable.name
    function isConstCached (node) {
      const cached = cache.get(node)
      if (cached !== undefined) {
        return cached
      }
      const res = _isConst(isConstCached, node, variableName)
      cache.set(node, res)
      return res
    }

    const res = _derivative(expr, isConstCached)
    return options.simplify ? simplify(res) : res
  }
Takes the derivative of an expression expressed in parser Nodes.
The derivative will be taken over the supplied variable in the
second parameter. If there are multiple variables in the expression,
it will return a partial derivative.

This uses rules of differentiation which can be found here:

- [Differentiation rules (Wikipedia)](https://en.wikipedia.org/wiki/Differentiation_rules)

Syntax:

    math.derivative(expr, variable)
    math.derivative(expr, variable, options)

Examples:

    math.derivative('x^2', 'x')                     // Node '2 * x'
    math.derivative('x^2', 'x', {simplify: false})  // Node '2 * 1 * x ^ (2 - 1)'
    math.derivative('sin(2x)', 'x'))                // Node '2 * cos(2 * x)'
    math.derivative('2*x', 'x').evaluate()          // number 2
    math.derivative('x^2', 'x').evaluate({x: 4})    // number 8
    const f = math.parse('x^2')
    const x = math.parse('x')
    math.derivative(f, x)                           // Node {2 * x}

See also:

    simplify, parse, evaluate

@param  {Node | string} expr           The expression to differentiate
@param  {SymbolNode | string} variable The variable over which to differentiate
@param  {{simplify: boolean}} [options]
                        There is one option available, `simplify`, which
                        is true by default. When false, output will not
                        be simplified.
@return {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode}    The derivative of `expr`