/// <summary>
        /// Differentiate the function with respect to the variable
        /// </summary>
        /// <param name="function"></param>
        /// <param name="variable"></param>
        /// <returns></returns>
        private static Expression Differentiate(Function function, Expression variable, BlockExpressionBuilder builder)
        {
            // note that one of the function parameters is the variable
            var expression = function.Expression;

            if (expression is BinaryExpression)
            {
                BinaryExpression binaryExpression = expression as BinaryExpression;
                if (expression.NodeType == ExpressionType.Multiply)
                {
                    if (binaryExpression.Left == variable && binaryExpression.Right == variable)
                    {
                        return(Expression.Multiply(new ConstantExpression <double>(2), variable));
                    }
                    if (binaryExpression.Left == variable)
                    {
                        return(binaryExpression.Right);
                    }
                    else if (binaryExpression.Right == variable)
                    {
                        return(binaryExpression.Left);
                    }
                }
                else if (expression.NodeType == ExpressionType.Add)
                {
                    if (binaryExpression.Left == variable && binaryExpression.Right == variable)
                    {
                        return(new ConstantExpression <double>(2));
                    }
                    else if (binaryExpression.Left == variable)
                    {
                        return(new ConstantExpression <double>(1));
                    }
                    else if (binaryExpression.Right == variable)
                    {
                        return(new ConstantExpression <double>(1));
                    }
                }
                else if (expression.NodeType == ExpressionType.Subtract)
                {
                    if (binaryExpression.Left == variable && binaryExpression.Right == variable)
                    {
                        return(new ConstantExpression <double>(0));
                    }
                    else if (binaryExpression.Left == variable)
                    {
                        return(new ConstantExpression <double>(1));
                    }
                    else if (binaryExpression.Right == variable)
                    {
                        return(new ConstantExpression <double>(-1));
                    }
                }
                else if (expression.NodeType == ExpressionType.Divide)
                {
                    if (binaryExpression.Left == variable && binaryExpression.Right == variable)
                    {
                        throw new NotImplementedException();                                                                          // should not happen
                    }
                    if (binaryExpression.Left == variable)
                    {
                        var right = binaryExpression.Right as ReferencingVectorParameterExpression <double>;
                        if (right.IsScalar)
                        {
                            return(builder.AddLocalAssignment <double>(1 / right.ScalarValue, new ScaleInverseExpression <double>(binaryExpression.Right as VectorParameterExpression, 1)));
                        }
                        else
                        {
                            return(builder.AddLocalAssignment <double>(new ScaleInverseExpression <double>(binaryExpression.Right as VectorParameterExpression, 1)));
                        }
                    }
                    else if (binaryExpression.Right == variable)
                    {
                        return(builder.AddNegateDivideExpression(function.Parameter, binaryExpression.Right as VectorParameterExpression)); // i.e. x/y => x/y * -1/y
                    }
                }
            }

            if (expression is UnaryMathsExpression)
            {
                var unaryExpression = expression as UnaryMathsExpression;
                if (unaryExpression.UnaryType == UnaryElementWiseOperation.ScaleOffset)
                {
                    return(new ConstantExpression <double>((unaryExpression as ScaleOffsetExpression <double>).Scale));
                }
                else if (unaryExpression.UnaryType == UnaryElementWiseOperation.Negate)
                {
                    return(new ConstantExpression <double>(-1));
                }
                else if (unaryExpression.UnaryType == UnaryElementWiseOperation.ScaleInverse)
                {
                    return(builder.AddNegateDivideExpression(function.Parameter, unaryExpression.Operand)); // i.e. x/y => x/y * -1/y
                }
                else if (unaryExpression.UnaryType == UnaryElementWiseOperation.Exp)
                {
                    return(function.Parameter);
                }
                else if (unaryExpression.UnaryType == UnaryElementWiseOperation.Log)
                {
                    return(builder.AddInverseExpression(unaryExpression.Operand));
                }
                else if (unaryExpression.UnaryType == UnaryElementWiseOperation.SquareRoot)
                {
                    return(builder.AddHalfInverseSquareRootExpression(unaryExpression.Operand));
                }
                else if (unaryExpression.UnaryType == UnaryElementWiseOperation.CumulativeNormal)
                {
                    return(builder.AddGaussian(unaryExpression.Operand));
                }
                else
                {
                    throw new NotImplementedException();
                }
            }
            else
            {
                throw new NotImplementedException();
            }
        }
        /// <summary>
        /// Differentiate the function with respect to the variable
        /// </summary>
        /// <param name="function"></param>
        /// <param name="variable"></param>
        /// <returns></returns>
        private static Expression Differentiate(Function function, Expression variable, BlockExpressionBuilder builder)
        {
            // note that one of the function parameters is the variable
            var expression = function.Expression;
            if (expression is BinaryExpression)
            {
                BinaryExpression binaryExpression = expression as BinaryExpression;
                if (expression.NodeType == ExpressionType.Multiply)
                {
                    if (binaryExpression.Left == variable && binaryExpression.Right == variable)
                    {
                        return Expression.Multiply(new ConstantExpression<double>(2), variable);
                    }
                    if (binaryExpression.Left == variable) return binaryExpression.Right;
                    else if (binaryExpression.Right == variable) return binaryExpression.Left;
                }
                else if (expression.NodeType == ExpressionType.Add)
                {
                    if (binaryExpression.Left == variable && binaryExpression.Right == variable)
                    {
                        return new ConstantExpression<double>(2);
                    }
                    else if (binaryExpression.Left == variable) return new ConstantExpression<double>(1);
                    else if (binaryExpression.Right == variable) return new ConstantExpression<double>(1);
                }
                else if (expression.NodeType == ExpressionType.Subtract)
                {
                    if (binaryExpression.Left == variable && binaryExpression.Right == variable)
                    {
                        return new ConstantExpression<double>(0);
                    }
                    else if (binaryExpression.Left == variable) return new ConstantExpression<double>(1);
                    else if (binaryExpression.Right == variable) return new ConstantExpression<double>(-1);
                }
                else if (expression.NodeType == ExpressionType.Divide)
                {
                    if (binaryExpression.Left == variable && binaryExpression.Right == variable) throw new NotImplementedException(); // should not happen
                    if (binaryExpression.Left == variable)
                    {
                        var right = binaryExpression.Right as ReferencingVectorParameterExpression<double>;
                        if (right.IsScalar) return builder.AddLocalAssignment<double>(1 / right.ScalarValue, new ScaleInverseExpression<double>(binaryExpression.Right as VectorParameterExpression, 1));
                        else return builder.AddLocalAssignment<double>(new ScaleInverseExpression<double>(binaryExpression.Right as VectorParameterExpression, 1));
                    }
                    else if (binaryExpression.Right == variable)
                    {
                        return builder.AddNegateDivideExpression(function.Parameter, binaryExpression.Right as VectorParameterExpression); // i.e. x/y => x/y * -1/y
                    }
                }
            }

            if (expression is UnaryMathsExpression)
            {
                var unaryExpression = expression as UnaryMathsExpression;
                if (unaryExpression.UnaryType == UnaryElementWiseOperation.ScaleOffset)
                {
                    return new ConstantExpression<double>((unaryExpression as ScaleOffsetExpression<double>).Scale);
                }
                else if (unaryExpression.UnaryType == UnaryElementWiseOperation.Negate) return new ConstantExpression<double>(-1);
                else if (unaryExpression.UnaryType == UnaryElementWiseOperation.ScaleInverse)
                {
                    return builder.AddNegateDivideExpression(function.Parameter, unaryExpression.Operand); // i.e. x/y => x/y * -1/y
                }
                else if (unaryExpression.UnaryType == UnaryElementWiseOperation.Exp) return function.Parameter;
                else if (unaryExpression.UnaryType == UnaryElementWiseOperation.Log) return builder.AddInverseExpression(unaryExpression.Operand);
                else if (unaryExpression.UnaryType == UnaryElementWiseOperation.SquareRoot) return builder.AddHalfInverseSquareRootExpression(unaryExpression.Operand);
                else if (unaryExpression.UnaryType == UnaryElementWiseOperation.CumulativeNormal) return builder.AddGaussian(unaryExpression.Operand);
                else throw new NotImplementedException();
            }
            else throw new NotImplementedException();
        }