Commit f50bceec authored by Brice Videau's avatar Brice Videau
Browse files

More bindings.

parent de245d6f
......@@ -4,3 +4,4 @@ require_relative 'cconfigspace/base'
require_relative 'cconfigspace/interval'
require_relative 'cconfigspace/rng'
require_relative 'cconfigspace/distribution'
require_relative 'cconfigspace/hyperparameter'
require 'singleton'
module CCS
extend FFI::Library
......@@ -35,8 +36,10 @@ module CCS
class MemoryPointer
alias read_ccs_float_t read_double
alias read_array_of_ccs_float_t read_array_of_double
alias write_array_of_ccs_float_t write_array_of_double
alias read_ccs_int_t read_int64
alias read_array_of_ccs_int_t read_array_of_int64
alias write_array_of_ccs_int_t write_array_of_int64
alias read_ccs_bool_t read_int32
alias read_ccs_result_t read_int32
alias read_ccs_hash_t read_uint32
......@@ -133,15 +136,90 @@ module CCS
class Value < FFI::Union
layout :f, :ccs_float_t,
:i, :ccs_int_t,
:s, :string,
:s, :pointer,
:o, :ccs_object_t
end
typedef Value.by_value, :ccs_value_t
class InactiveClass
include Singleton
end
Inactive = InactiveClass.instance
class Datum < FFI::Struct
layout :value, :ccs_value_t,
:type, :ccs_data_type_t
end
class Datum
NONE = self::new
NONE[:type] = :CCS_NONE
NONE[:value][:i] = 0
TRUE = self::new
TRUE[:type] = :CCS_BOOLEAN
TRUE[:value][:i] = CCS::TRUE
FALSE = self::new
FALSE[:type] = :CCS_BOOLEAN
FALSE[:value][:i] = CCS::FALSE
INACTIVE = self::new
INACTIVE[:type] = :CCS_INACTIVE
INACTIVE[:value][:i] = 0
def value
case self[:type]
when :NONE
nil
when :CCS_INTEGER
self[:value][:i]
when :CCS_FLOAT
self[:value][:f]
when :CCS_BOOLEAN
self[:value][:i] == :CCS_FALSE ? false : true
when :CCS_STRING
self[:value][:s].read_string
when :CCS_INACTIVE
Inactive
when :CCS_OBJECT
Object::from_handle(self[:value][:o])
end
end
def self.from_value(v)
case v
when nil
NONE
when true
TRUE
when false
FALSE
when Inactive
INACTIVE
when Float
d = self::new
d[:type] = :CCS_FLOAT
d[:value][:f] = v
d
when Integer
d = self::new
d[:type] = :CCS_INTEGER
d[:value][:i] = v
d
when String
d = self::new
ptr = MemoryPointer::from_string(v)
d.instance_variable_set(:@string, ptr)
d[:type] = :STRING
d[:valus][:s] = ptr
d
when Object
d = self::new
d[:type] = :CCS_OBJECT
d[:value][:o] = v.handle
d.instance_variable_set(:@object, v)
d
else
raise StandardError, :CCS_INVALID_VALUE
end
end
end
typedef Datum.by_value, :ccs_datum_t
attach_function :ccs_init, [], :ccs_result_t
......@@ -177,48 +255,63 @@ module CCS
CCS.ccs_release_object(@handle)
end
end
def self.add_property(name, type, accessor, memoize: false)
src = ""
src << "def #{name}\n"
src << " @#{name} ||= begin\n" if memoize
src << " ptr = MemoryPointer::new(:#{type})\n"
src << " res = CCS.#{accessor}(@handle, ptr)\n"
src << " CCS.error_check(res)\n"
src << " ptr.read_#{type}\n"
src << " end\n" if memoize
src << "end\n"
class_eval src
end
add_property :object_type, :ccs_object_type_t, :ccs_object_get_type, memoize: true
add_property :refcount, :uint32, :ccs_object_get_refcount
attr_reader :handle
def initialize(handle, retain: false)
def initialize(handle, retain: false, auto_release: true)
@handle = handle
if retain
res = CCS.ccs_retain_object(handle)
CCS.error_check(res)
end
ObjectSpace.define_finalizer(self, Releaser::new(handle))
ObjectSpace.define_finalizer(self, Releaser::new(handle)) if auto_release
end
def object_type
def self.from_handle(handle)
ptr = MemoryPointer::new(:ccs_object_type_t)
res = CCS.ccs_object_get_type(@handle, ptr)
CCS.error_check(res)
return ptr.read_ccs_object_type_t
end
def refcount
ptr = MemoryPointer::new(:int32)
res = CCS.ccs_object_get_refcount(@handle, ptr)
res = CCS.ccs_object_get_type(handle, ptr)
CCS.error_check(res)
return ptr.read_int32
case ptr.read_ccs_object_type_t
when :CCS_RNG
CCS::Rng::from_handle(handle)
when :CCS_DISTRIBUTION
CCS::Distribution::from_handle(handle)
when :CCS_HYPERPARAMETER
CCS::Hyperparameter::from_handle(handle)
when :CCS_EXPRESSION
CCS::Expression::from_handle(handle)
when :CCS_CONFIGURATION_SPACE
CCS::ConfigurationSpace::from_handle(handle)
when :CCS_CONFIGURATION
CCS::Configuration::from_handle(handle)
when :CCS_OBJECTIVE_SPACE
CCS::ObjectiveSpace::from_handle(handle)
when :CCS_EVALUATION
CCS::Evaluation::from_handle(handle)
when :CCS_TUNER
CCS::Tuner::from_handle(handle)
else
raise StandardError, :CCS_INVALID_OBJECT
end
end
def to_ptr
@handle
end
def self.add_property(name, type, accessor, memoize: false)
src = ""
src << "def #{name}\n"
src << " @#{name} ||= begin\n" if memoize
src << " ptr = MemoryPointer::new(:#{type})\n"
src << " res = CCS.#{accessor}(@handle, ptr)\n"
src << " CCS.error_check(res)\n"
src << " ptr.read_#{type}\n"
src << " end\n" if memoize
src << "end\n"
class_eval src
end
end
end
......@@ -27,7 +27,7 @@ module CCS
attach_function :ccs_distribution_get_scale_type, [:ccs_distribution_t, :pointer], :ccs_result_t
attach_function :ccs_distribution_get_quantization, [:ccs_distribution_t, :pointer], :ccs_result_t
attach_function :ccs_distribution_get_bounds, [:ccs_distribution_t, :pointer], :ccs_result_t
attach_function :ccs_distribution_check_oversampling, [:ccs_distribution_t, Interval.by_ref], :ccs_result_t
attach_function :ccs_distribution_check_oversampling, [:ccs_distribution_t, Interval.by_ref, :pointer], :ccs_result_t
attach_function :ccs_distribution_sample, [:ccs_distribution_t, :ccs_rng_t, :pointer], :ccs_result_t
attach_function :ccs_distribution_samples, [:ccs_distribution_t, :ccs_rng_t, :size_t, :pointer], :ccs_result_t
......@@ -35,7 +35,7 @@ module CCS
add_property :type, :ccs_distribution_type_t, :ccs_distribution_get_type, memoize: true
add_property :data_type, :ccs_numeric_type_t, :ccs_distribution_get_data_type, memoize: true
add_property :dimension, :size_t, :ccs_distribution_get_dimension, memoize: true
add_property :scale_type, :ccs_scale_type_t, :ccs_distribution_get_scale_type, memoize: true
add_property :scale, :ccs_scale_type_t, :ccs_distribution_get_scale_type, memoize: true
def initialize(handle, retain: false)
if !handle
......@@ -44,6 +44,22 @@ module CCS
super
end
def self.from_handle(handle)
ptr = MemoryPointer::new(:ccs_distribution_type_t)
res = CCS.ccs_distribution_get_type(handle, ptr)
CCS.error_check(res)
case ptr.read_ccs_distribution_type_t
when :CCS_UNIFORM
UniformDistribution::new(handle, retain: true)
when :CCS_NORMAL
NormalDistribution::new(handle, retain: true)
when :CCS_ROULETTE
RouletteDistribution::new(handle, retain: true)
else
raise StandardError, :CCS_INVALID_DISTRIBUTION
end
end
def quantization
@quantization ||= begin
ptr = MemoryPointer::new(:ccs_numeric_t)
......@@ -100,22 +116,132 @@ module CCS
attach_function :ccs_create_uniform_distribution, [:ccs_numeric_type_t, :ccs_numeric_t, :ccs_numeric_t, :ccs_scale_type_t, :ccs_numeric_t, :pointer], :ccs_result_t
attach_function :ccs_create_uniform_int_distribution, [:ccs_int_t, :ccs_int_t, :ccs_scale_type_t, :ccs_int_t, :pointer], :ccs_result_t
attach_function :ccs_create_uniform_float_distribution, [:ccs_float_t, :ccs_float_t, :ccs_scale_type_t, :ccs_float_t, :pointer], :ccs_result_t
attach_function :ccs_uniform_distribution_get_parameters, [:ccs_distribution_t, :pointer, :pointer], :ccs_result_t
class UniformDistribution < Distribution
def initialize(handle = nil, retain: false, data_type: :CCS_NUM_FLOAT, lower: 0.0, upper: 1.0, scale_type: :CCS_LINEAR, quantization: 0.0)
def initialize(handle = nil, retain: false, data_type: :CCS_NUM_FLOAT, lower: 0.0, upper: 1.0, scale: :CCS_LINEAR, quantization: 0.0)
if handle
super(handle, retain: retain)
else
ptr = MemoryPointer::new(:ccs_distribution_t)
res = if data_type == :CCS_NUM_FLOAT
CCS.ccs_create_uniform_float_distribution(lower, upper, scale, quantization, ptr)
else
CCS.ccs_create_uniform_int_distribution(lower, upper, scale, quantization, ptr)
end
CCS.error_check(res)
super(ptr.read_pointer, retain: false)
end
end
def self.int(lower:, upper:, scale: :CCS_LINEAR, quantization: 0)
self.new(nil, data_type: :CCS_NUM_INTEGER, lower: lower, upper: upper, scale: scale, quantization: quantization)
end
def self.float(lower:, upper:, scale: :CCS_LINEAR, quantization: 0.0)
self.new(nil, data_type: :CCS_NUM_FLOAT, lower: lower, upper: upper, scale: scale, quantization: quantization)
end
def lower
@lower ||= begin
ptr = MemoryPointer::new(:ccs_numeric_t)
res = CCS.ccs_uniform_distribution_get_parameters(@handle, ptr, nil)
CCS.error_check(res)
if data_type == :CCS_NUM_FLOAT
ptr.read_ccs_float_t
else
ptr.read_ccs_int_t
end
end
end
def upper
@upper ||= begin
ptr = MemoryPointer::new(:ccs_numeric_t)
res = CCS.ccs_uniform_distribution_get_parameters(@handle, nil, ptr)
CCS.error_check(res)
if data_type == :CCS_NUM_FLOAT
ptr.read_ccs_float_t
else
ptr.read_ccs_int_t
end
end
end
end
attach_function :ccs_create_normal_distribution, [:ccs_numeric_type_t, :ccs_float_t, :ccs_float_t, :ccs_scale_type_t, :ccs_numeric_t, :pointer], :ccs_result_t
attach_function :ccs_create_normal_int_distribution, [:ccs_float_t, :ccs_float_t, :ccs_scale_type_t, :ccs_int_t, :pointer], :ccs_result_t
attach_function :ccs_create_normal_float_distribution, [:ccs_float_t, :ccs_float_t, :ccs_scale_type_t, :ccs_float_t, :pointer], :ccs_result_t
attach_function :ccs_normal_distribution_get_parameters, [:ccs_distribution_t, :pointer, :pointer], :ccs_result_t
class NormalDistribution < Distribution
def initialize(handle = nil, retain: false, data_type: :CCS_NUM_FLOAT, mu: 0.0, sigma: 1.0, scale: :CCS_LINEAR, quantization: 0.0)
if handle
super(handle, retain: retian)
super(handle, retain: retain)
else
ptr = MemoryPointer::new(:ccs_distribution_t)
res = if data_type == :CCS_NUM_FLOAT
CCS.ccs_create_uniform_float_distribution(lower, upper, scale_type, quantization, ptr)
CCS.ccs_create_normal_float_distribution(mu, sigma, scale, quantization, ptr)
else
CCS.ccs_create_uniform_int_distribution(lower, upper, scale_type, quantization, ptr)
CCS.ccs_create_normal_int_distribution(mu, sigma, scale, quantization, ptr)
end
CCS.error_check(res)
super(ptr.read_pointer, retain: false)
end
end
def self.int(mu:, sigma:, scale: :CCS_LINEAR, quantization: 0)
self::new(nil, retain: false, data_type: :CCS_NUM_INTEGER, mu: mu, sigma: sigma, scale: scale, quantization: quantization)
end
def self.float(mu:, sigma:, scale: :CCS_LINEAR, quantization: 0)
self::new(nil, retain: false, data_type: :CCS_NUM_FLOAT, mu: mu, sigma: sigma, scale: scale, quantization: quantization)
end
def mu
@mu ||= begin
ptr = MemoryPointer::new(:ccs_numeric_t)
res = CCS.ccs_normal_distribution_get_parameters(@handle, ptr, nil)
CCS.error_check(res)
ptr.read_ccs_float_t
end
end
def sigma
@sigma ||= begin
ptr = MemoryPointer::new(:ccs_numeric_t)
res = CCS.ccs_normal_distribution_get_parameters(@handle, nil, ptr)
CCS.error_check(res)
ptr.read_ccs_float_t
end
end
end
attach_function :ccs_create_roulette_distribution, [:size_t, :pointer, :pointer], :ccs_result_t
attach_function :ccs_roulette_distribution_get_num_areas, [:ccs_distribution_t, :pointer], :ccs_result_t
attach_function :ccs_roulette_distribution_get_areas, [:ccs_distribution_t, :size_t, :pointer, :pointer], :ccs_result_t
class RouletteDistribution < Distribution
add_property :num_areas, :size_t, :ccs_roulette_distribution_get_num_areas, memoize: true
def initialize(handle = nil, retain: false, areas: [])
if handle
super(handle, retain: retain)
else
ptr = MemoryPointer::new(:ccs_distribution_t)
p_areas = MemoryPointer::new(:ccs_float_t, areas.size)
p_areas.write_array_of_ccs_float_t(areas)
res = CCS.ccs_create_roulette_distribution(areas.size, p_areas, ptr)
CCS.error_check(res)
super(ptr.read_pointer, retain: false)
end
end
def areas
@areas ||= begin
count = num_areas
ptr = MemoryPointer::new(:ccs_float_t, count)
res = CCS.ccs_roulette_distribution_get_areas(@handle, count, ptr, nil)
CCS.error_check(res)
ptr.read_array_of_ccs_float_t(count)
end
end
end
end
module CCS
HyperparameterType = enum FFI::Type::INT32, :ccs_hyperparameter_type_t, [
:CCS_NUMERICAL,
:CCS_CATEGORICAL,
:CCS_ORDINAL
]
class MemoryPointer
def read_ccs_hyperparameter_type_t
HyperparameterType.from_native(read_int32, nil)
end
end
attach_function :ccs_hyperparameter_get_type, [:ccs_hyperparameter_t, :pointer], :ccs_result_t
attach_function :ccs_hyperparameter_get_default_value, [:ccs_hyperparameter_t, :pointer], :ccs_result_t
attach_function :ccs_hyperparameter_get_name, [:ccs_hyperparameter_t, :pointer], :ccs_result_t
attach_function :ccs_hyperparameter_get_user_data, [:ccs_hyperparameter_t, :pointer], :ccs_result_t
attach_function :ccs_hyperparameter_get_default_distribution, [:ccs_hyperparameter_t, :pointer], :ccs_result_t
attach_function :ccs_hyperparameter_check_value, [:ccs_hyperparameter_t, :ccs_datum_t, :pointer], :ccs_result_t
attach_function :ccs_hyperparameter_check_values, [:ccs_hyperparameter_t, :size_t, :pointer, :pointer], :ccs_result_t
class Hyperparameter < Object
add_property :type, :ccs_distribution_type_t, :ccs_hyperparameter_get_type, memoize:true
add_property :user_data, :pointer, :ccs_hyperparameter_get_user_data, memoize: true
def initialize(handle, retain: false)
if !handle
raise StandardError, :CCS_INVALID_OBJECT
end
super
end
def self.from_handle(handle)
ptr = MemoryPointer::new(:ccs_hyperparameter_type_t)
res = CCS.ccs_hyperparameter_get_type(handle, ptr)
CCS.error_check(res)
case ptr.read_ccs_hyperparameter_type_t
when :CCS_NUMERICAL
NumericalHyperparameter::new(handle, retain: true)
when :CCS_CATEGORICAL
CategoricalHyperparameter::new(handle, retain: true)
when :CCS_ORDINAL
OrdinalHyperparameter::new(handle, retain: true)
else
raise StandardError, :CCS_INVALID_HYPERPARAMETER
end
end
def name
@name ||= begin
ptr = MemoryPointer::new(:pointer)
res = CCS.ccs_hyperparameter_get_name(@handle, ptr)
CCS.error_check(res)
ptr.read_pointer.read_string
end
end
def default_value
@default_value ||= begin
ptr = MemoryPointer::new(:ccs_datum_t)
res = CCS.ccs_hyperparameter_get_default_value(handle, ptr)
CCS.error_check(res)
d = Datum::new(ptr)
d.value
end
end
def default_distribution
@default_distribution ||= begin
ptr = MemoryPointer::new(:ccs_distribution_t)
res = CCS.ccs_hyperparameter_get_default_distribution(handle, ptr)
CCS.error_check(res)
Object::from_handle(ptr.read_pointer)
end
end
end
end
......@@ -23,7 +23,7 @@ module CCS
if type == :CCS_NUM_FLOAT
self[:upper][:f] = upper
else
self[:lower][:i] = upper
self[:upper][:i] = upper
end
end
self[:lower_included] = lower_included ? CCS::TRUE : CCS::FALSE
......@@ -128,6 +128,13 @@ module CCS
res = CCS.ccs_interval_include(self, n)
res == CCS::FALSE ? false : true
end
def to_s
s = ""
s << (lower_included? ? "[" : "(")
s << " #{lower}, #{upper} "
s << (upper_included? ? "]" : ")")
end
end
typedef Interval.by_value, :ccs_interval_t
......
......@@ -21,6 +21,10 @@ module CCS
end
end
def self.from_handle(handle)
self.new(handle, retain: true)
end
def seed=(s)
res = CCS.ccs_rng_set_seed(@handle, s)
CCS.error_check(res)
......
......@@ -11,4 +11,53 @@ class CConfigSpaceTest < Minitest::Test
ver = CCS.version
assert(ver.kind_of?(CCS::Version))
end
def test_datum_value
d = CCS::Datum::new
d[:type] = :CCS_NONE
assert_nil( d.value )
d[:type] = :CCS_INACTIVE
assert_equal( CCS::Inactive, d.value )
d[:type] = :CCS_FLOAT
d[:value][:f] = 15.0
assert_equal( 15.0, d.value )
d[:type] = :CCS_INTEGER
d[:value][:i] = 15
assert_equal( 15, d.value )
ptr = CCS::MemoryPointer::from_string("foo")
d[:type] = :CCS_STRING
d[:value][:s] = ptr
assert_equal( "foo", d.value )
rng = CCS::Rng::new
d[:type] = :CCS_OBJECT
d[:value][:o] = rng.handle
o = d.value
assert( o.kind_of? CCS::Rng )
assert_equal( :CCS_RNG, d.value.object_type )
end
def test_from_value
d = CCS::Datum::from_value(nil)
assert_equal( :CCS_NONE, d[:type] )
assert_equal( 0, d[:value][:i] )
d = CCS::Datum::from_value(CCS::Inactive)
assert_equal( :CCS_INACTIVE, d[:type] )
assert_equal( 0, d[:value][:i] )
d = CCS::Datum::from_value(false)
assert_equal( :CCS_BOOLEAN, d[:type] )
assert_equal( CCS::FALSE, d[:value][:i] )
d = CCS::Datum::from_value(true)
assert_equal( :CCS_BOOLEAN, d[:type] )
assert_equal( CCS::TRUE, d[:value][:i] )
d = CCS::Datum::from_value(15)
assert_equal( :CCS_INTEGER, d[:type] )
assert_equal( 15, d[:value][:i] )
d = CCS::Datum::from_value(15.0)
assert_equal( :CCS_FLOAT, d[:type] )
assert_equal( 15.0, d[:value][:f] )
rng = CCS::Rng::new
d = CCS::Datum::from_value(rng)
assert_equal( :CCS_OBJECT, d[:type] )
assert_equal( rng.handle, d[:value][:o] )
end
end
......@@ -7,21 +7,169 @@ class CConfigSpaceTestDistribution < Minitest::Test
CCS.init
end
def test_from_handle_roulette
areas = [ 1.0, 2.0, 1.0, 0.5 ]
d = CCS::RouletteDistribution::new(areas: areas)
d2 = CCS::Object::from_handle(d)
assert_equal( d.class, d2.class )
end
def test_create_roulette
areas = [ 1.0, 2.0, 1.0, 0.5 ]
sum = areas.reduce(:+)
d = CCS::RouletteDistribution::new(areas: areas)
assert_equal( :CCS_DISTRIBUTION, d.object_type )
assert_equal( :CCS_ROULETTE, d.type )
assert_equal( :CCS_NUM_INTEGER, d.data_type )
assert_equal( :CCS_LINEAR, d.scale )
assert_equal( 1, d.dimension )
assert_equal( 4, d.num_areas )
assert( d.areas.reduce(:+) > 0.999 )
assert( d.areas.reduce(:+) < 1.001 )
d.areas.each_with_index { |a, i|
assert( a < areas[i] * 1.001 / sum && a > areas[i] * 0.999 / sum )
}
i = d.bounds
assert_equal( :CCS_NUM_INTEGER, i.type)
assert_equal( 0, i.lower)
assert_equal( 4, i.upper)
assert( i.lower_included? )
refute( i.upper_included? )
end
def test_from_handle_normal
d = CCS::NormalDistribution::new
d2 = CCS::Object::from_handle(d)
assert_equal( d.class, d2.class )
end
def test_create_normal
d = CCS::NormalDistribution::new
assert( d.object_type == :CCS_DISTRIBUTION )
assert( d.type == :CCS_NORMAL )
assert( d.data_type == :CCS_NUM_FLOAT )
assert( d.scale == :CCS_LINEAR )