class Keisan::Functions::Diff

Public Class Methods

new() click to toggle source
Calls superclass method Keisan::Function::new
# File lib/keisan/functions/diff.rb, line 4
def initialize
  super("diff", -1)
  @name = "diff"
end

Public Instance Methods

evaluate(ast_function, context = nil) click to toggle source
# File lib/keisan/functions/diff.rb, line 21
def evaluate(ast_function, context = nil)
  validate_arguments!(ast_function.children.count)
  context ||= Context.new
  function, variables = function_and_variables(ast_function)
  local = context_from(variables, context)

  result = variables.inject(function.evaluate(local)) do |result, variable|
    result = differentiate(result, variable, local)
    if !is_ast_derivative?(result)
      result = result.evaluate(local)
    end
    result
  end

  case result
  when AST::Function
    result.name == "diff" ? result : result.simplify(context)
  else
    result.simplify(context)
  end
end
simplify(ast_function, context = nil) click to toggle source
# File lib/keisan/functions/diff.rb, line 43
def simplify(ast_function, context = nil)
  validate_arguments!(ast_function.children.count)
  raise Exceptions::InternalError.new("received non-diff function") unless ast_function.name == "diff"
  function, variables = function_and_variables(ast_function)
  context ||= Context.new
  local = context_from(variables, context)

  result = variables.inject(function.simplify(local)) do |result, variable|
    result = differentiate(result, variable, local)
    if !is_ast_derivative?(result)
      result = result.simplify(local)
    end
    result
  end

  case result
  when AST::Function
    result.name == "diff" ? result : result.simplify(context)
  else
    result.simplify(context)
  end
end
value(ast_function, context = nil) click to toggle source
# File lib/keisan/functions/diff.rb, line 9
def value(ast_function, context = nil)
  validate_arguments!(ast_function.children.count)
  context ||= Context.new
  evaluation = evaluate(ast_function, context)

  if is_ast_derivative?(evaluation)
    raise Exceptions::NonDifferentiableError.new
  else
    evaluation.value(context)
  end
end

Private Instance Methods

context_from(variables, context = nil) click to toggle source
# File lib/keisan/functions/diff.rb, line 102
def context_from(variables, context = nil)
  context ||= Context.new(shadowed: variables.map(&:name))
  context.spawn_child(shadowed: variables.map(&:name))
end
differentiate(node, variable, context) click to toggle source
# File lib/keisan/functions/diff.rb, line 72
def differentiate(node, variable, context)
  if node.unbound_variables(context).include?(variable.name)
    node.differentiate(variable, context)
  else
    return AST::Number.new(0)
  end
rescue Exceptions::NonDifferentiableError => e
  return AST::Function.new(
    [node, variable],
    "diff"
  )
end
function_and_variables(ast_function) click to toggle source
# File lib/keisan/functions/diff.rb, line 85
def function_and_variables(ast_function)
  unless ast_function.is_a?(AST::Function) && ast_function.name == name
    raise Exceptions::InvalidFunctionError.new("Must receive diff function")
  end

  variables = ast_function.children[1..-1]

  unless variables.all? {|var| var.is_a?(AST::Variable)}
    raise Exceptions::InvalidFunctionError.new("Diff must differentiate with respect to variables")
  end

  [
    ast_function.children.first,
    variables
  ]
end
is_ast_derivative?(node) click to toggle source
# File lib/keisan/functions/diff.rb, line 68
def is_ast_derivative?(node)
  node.is_a?(AST::Function) && node.name == name
end