class Neighbor::Vector

Public Class Methods

cast(value, dimensions:, normalize:, column_info:) click to toggle source
# File lib/neighbor/vector.rb, line 11
def self.cast(value, dimensions:, normalize:, column_info:)
  value = value.to_a.map(&:to_f)

  dimensions ||= column_info[:dimensions]
  raise Error, "Expected #{dimensions} dimensions, not #{value.size}" if dimensions && value.size != dimensions

  raise Error, "Values must be finite" unless value.all?(&:finite?)

  if normalize
    norm = Math.sqrt(value.sum { |v| v * v })

    # store zero vector as all zeros
    # since NaN makes the distance always 0
    # could also throw error

    # safe to update in-place since earlier map dups
    value.map! { |v| v / norm } if norm > 0
  end

  value
end
column_info(model, attribute_name) click to toggle source
# File lib/neighbor/vector.rb, line 33
def self.column_info(model, attribute_name)
  attribute_name = attribute_name.to_s
  column = model.columns.detect { |c| c.name == attribute_name }
  {
    type: column.try(:type),
    dimensions: column.try(:limit)
  }
end
new(dimensions:, normalize:, model:, attribute_name:) click to toggle source
Calls superclass method
# File lib/neighbor/vector.rb, line 3
def initialize(dimensions:, normalize:, model:, attribute_name:)
  super()
  @dimensions = dimensions
  @normalize = normalize
  @model = model
  @attribute_name = attribute_name
end

Public Instance Methods

cast(value) click to toggle source
# File lib/neighbor/vector.rb, line 47
def cast(value)
  self.class.cast(value, dimensions: @dimensions, normalize: @normalize, column_info: column_info) unless value.nil?
end
column_info() click to toggle source

need to be careful to avoid loading column info before needed

# File lib/neighbor/vector.rb, line 43
def column_info
  @column_info ||= self.class.column_info(@model, @attribute_name)
end
deserialize(value) click to toggle source
# File lib/neighbor/vector.rb, line 61
def deserialize(value)
  value[1..-1].split(",").map(&:to_f) unless value.nil?
end
serialize(value) click to toggle source
# File lib/neighbor/vector.rb, line 51
def serialize(value)
  unless value.nil?
    if column_info[:type] == :vector
      "[#{cast(value).join(", ")}]"
    else
      "(#{cast(value).join(", ")})"
    end
  end
end