class Ngt::Index

Attributes

path[R]

Public Class Methods

create(path, dimensions, **options) click to toggle source
# File lib/ngt/index.rb, line 181
def self.create(path, dimensions, **options)
  warn "[ngt] create is deprecated - use new instead"
  new(dimensions, path: path, **options)
end
ffi(*args) click to toggle source

private

# File lib/ngt/index.rb, line 187
def self.ffi(*args)
  Utils.ffi(*args)
end
finalize(error, index, property) click to toggle source
# File lib/ngt/index.rb, line 191
def self.finalize(error, index, property)
  # must use proc instead of stabby lambda
  proc do
    FFI.ngt_destroy_error_object(error)
    FFI.ngt_close_index(index)
    FFI.ngt_destroy_property(property)
  end
end
load(path) click to toggle source
# File lib/ngt/index.rb, line 177
def self.load(path)
  new(nil, path: path)
end
new(index, path) click to toggle source
# File lib/ngt/index.rb, line 7
def initialize(index, path)
  @index = index
  @path = path

  @error = FFI.ngt_create_error_object
  @property = ffi(:ngt_create_property)
  ffi(:ngt_get_property, @index, @property)

  ObjectSpace.define_finalizer(self, self.class.finalize(@error, @index, @property))
end
new(dimensions, path: nil, edge_size_for_creation: 10, edge_size_for_search: 40, object_type: :float, distance_type: :l2) click to toggle source
Calls superclass method
# File lib/ngt/index.rb, line 113
def self.new(dimensions, path: nil, edge_size_for_creation: 10,
    edge_size_for_search: 40, object_type: :float, distance_type: :l2)

  error = FFI.ngt_create_error_object

  # TODO remove in 0.4.0
  if !dimensions.is_a?(Integer) && !path
    warn "[ngt] Passing a path to new is deprecated - use load instead"
    path = dimensions
    dimensions = nil
  end

  if path && dimensions.nil?
    index = ffi(:ngt_open_index, path, error)
  else
    property = ffi(:ngt_create_property, error)
    ffi(:ngt_set_property_dimension, property, dimensions, error)
    ffi(:ngt_set_property_edge_size_for_creation, property, edge_size_for_creation, error)
    ffi(:ngt_set_property_edge_size_for_search, property, edge_size_for_search, error)

    case object_type.to_s.downcase
    when "float"
      ffi(:ngt_set_property_object_type_float, property, error)
    when "integer"
      ffi(:ngt_set_property_object_type_integer, property, error)
    else
      raise ArgumentError, "Unknown object type: #{object_type}"
    end

    case distance_type.to_s.downcase
    when "l1"
      ffi(:ngt_set_property_distance_type_l1, property, error)
    when "l2"
      ffi(:ngt_set_property_distance_type_l2, property, error)
    when "angle"
      ffi(:ngt_set_property_distance_type_angle, property, error)
    when "hamming"
      ffi(:ngt_set_property_distance_type_hamming, property, error)
    when "jaccard"
      ffi(:ngt_set_property_distance_type_jaccard, property, error)
    when "cosine"
      ffi(:ngt_set_property_distance_type_cosine, property, error)
    when "normalized_angle"
      ffi(:ngt_set_property_distance_type_normalized_angle, property, error)
    when "normalized_cosine"
      ffi(:ngt_set_property_distance_type_normalized_cosine, property, error)
    else
      raise ArgumentError, "Unknown distance type: #{distance_type}"
    end

    index =
      if path
        ffi(:ngt_create_graph_and_tree, path, property, error)
      else
        ffi(:ngt_create_graph_and_tree_in_memory, property, error)
      end
  end

  super(index, path)
ensure
  FFI.ngt_destroy_error_object(error) if error
  FFI.ngt_destroy_property(property) if property
end

Public Instance Methods

batch_insert(objects, num_threads: 8) click to toggle source
# File lib/ngt/index.rb, line 45
def batch_insert(objects, num_threads: 8)
  if narray?(objects)
    objects = objects.cast_to(Numo::SFloat) unless objects.is_a?(Numo::SFloat)
    count = objects.shape[0]
    obj = ::FFI::MemoryPointer.new(:char, objects.byte_size)
    obj.write_bytes(objects.to_binary)
  else
    objects = objects.to_a
    count = objects.size
    flat_objects = objects.flatten
    obj = ::FFI::MemoryPointer.new(:float, flat_objects.size)
    obj.write_array_of_float(flat_objects)
  end

  ids = ::FFI::MemoryPointer.new(:uint32, count)
  ffi(:ngt_batch_insert_index, @index, obj, count, ids)

  build_index(num_threads: num_threads)

  ids.read_array_of_uint32(count)
end
build_index(num_threads: 8) click to toggle source
# File lib/ngt/index.rb, line 67
def build_index(num_threads: 8)
  ffi(:ngt_create_index, @index, num_threads)
end
close() click to toggle source
# File lib/ngt/index.rb, line 109
def close
  FFI.ngt_close_index(@index)
end
dimensions() click to toggle source
# File lib/ngt/index.rb, line 18
def dimensions
  @dimensions ||= ffi(:ngt_get_property_dimension, @property)
end
distance_type() click to toggle source
# File lib/ngt/index.rb, line 22
def distance_type
  @distance_type ||= ffi(:ngt_get_property_distance_type, @property)
end
edge_size_for_creation() click to toggle source
# File lib/ngt/index.rb, line 26
def edge_size_for_creation
  @edge_size_for_creation ||= ffi(:ngt_get_property_edge_size_for_creation, @property)
end
insert(object) click to toggle source
# File lib/ngt/index.rb, line 41
def insert(object)
  ffi(:ngt_insert_index, @index, c_object(object.to_a), dimensions)
end
object(id) click to toggle source
# File lib/ngt/index.rb, line 71
def object(id)
  if object_type == :float
    res = ffi(:ngt_get_object_as_float, @object_space, id)
    res.read_array_of_float(dimensions)
  else
    res = ffi(:ngt_get_object_as_integer, @object_space, id)
    res.read_array_of_uint8(dimensions)
  end
end
object_type() click to toggle source
# File lib/ngt/index.rb, line 34
def object_type
  @object_type ||= begin
    object_type = ffi(:ngt_get_property_object_type, @property)
    FFI.ngt_is_property_object_type_float(object_type) ? :float : :integer
  end
end
remove(id) click to toggle source
# File lib/ngt/index.rb, line 81
def remove(id)
  ffi(:ngt_remove_index, @index, id)
end
save(path2 = nil, path: nil) click to toggle source
# File lib/ngt/index.rb, line 103
def save(path2 = nil, path: nil)
  warn "[ngt] Passing path as an option is deprecated - use an argument instead" if path
  @path = path || path2 || @path || Dir.mktmpdir
  ffi(:ngt_save_index, @index, @path)
end

Private Instance Methods

c_object(object) click to toggle source
# File lib/ngt/index.rb, line 206
def c_object(object)
  c_object = ::FFI::MemoryPointer.new(:double, object.size)
  c_object.write_array_of_double(object)
  c_object
end
narray?(data) click to toggle source
# File lib/ngt/index.rb, line 202
def narray?(data)
  defined?(Numo::NArray) && data.is_a?(Numo::NArray)
end