Commit 816c6c18 authored by Brice Videau's avatar Brice Videau
Browse files

Demonstrated user defined tuner in ruby.

parent 8ae19fcd
......@@ -35,7 +35,7 @@ module CCS
when :CCS_TUNER_RANDOM
RandomTuner::new(handle, retain: true)
when :CCS_TUNER_USER_DEFINED
GenericTuner::new(handle, retain: true)
UserDefinedTuner::new(handle, retain: true)
else
raise CCSError, :CCS_INVALID_TUNER
end
......@@ -111,4 +111,136 @@ module CCS
end
end
end
class TunerCommonData < FFI::Struct
layout :type, :ccs_tuner_type_t,
:name, :string,
:user_data, :pointer,
:configuration_space, :ccs_configuration_space_t,
:objective_space, :ccs_objective_space_t
end
typedef TunerCommonData.by_value, :ccs_tuner_common_data_t
class UserDefinedTunerData < FFI::Struct
end
callback :ccs_user_defined_tuner_del, [UserDefinedTunerData.by_ref], :ccs_result_t
callback :ccs_user_defined_tuner_ask, [UserDefinedTunerData.by_ref, :size_t, :pointer, :pointer], :ccs_result_t
callback :ccs_user_defined_tuner_tell, [UserDefinedTunerData.by_ref, :size_t, :pointer], :ccs_result_t
callback :ccs_user_defined_tuner_get_optimums, [UserDefinedTunerData.by_ref, :size_t, :pointer, :pointer], :ccs_result_t
callback :ccs_user_defined_tuner_get_history, [UserDefinedTunerData.by_ref, :size_t, :pointer, :pointer], :ccs_result_t
class UserDefinedTunerVector < FFI::Struct
layout :del, :ccs_user_defined_tuner_del,
:ask, :ccs_user_defined_tuner_ask,
:tell, :ccs_user_defined_tuner_tell,
:get_optimums, :ccs_user_defined_tuner_get_optimums,
:get_history, :ccs_user_defined_tuner_get_history
end
typedef UserDefinedTunerVector.by_value, :ccs_user_defined_tuner_vector_t
class UserDefinedTunerData
layout :common_data, :ccs_tuner_common_data_t,
:vector, :ccs_user_defined_tuner_vector_t,
:tuner_data, :pointer
end
typedef UserDefinedTunerData.by_value, :ccs_user_defined_tuner_data_t
attach_function :ccs_create_user_defined_tuner, [:string, :ccs_configuration_space_t, :ccs_objective_space_t, :pointer, UserDefinedTunerVector.by_ref, :pointer, :pointer], :ccs_result_t
class UserDefinedTuner < Tuner
@callbacks = {}
class << self
attr_reader :callbacks
end
def initialize(handle = nil, retain: false, name: nil, configuration_space: nil, objective_space: nil, user_data: nil, del: nil, ask: nil, tell: nil, get_optimums: nil, get_history: nil, tuner_data: nil)
if handle
super(handle, retain: retain)
else
raise CCSError, :CCS_INVALID_VALUE if del.nil? || ask.nil? || tell.nil? || get_optimums.nil? || get_history.nil?
delwrapper = lambda { |data|
begin
del.call(data)
UserDefinedTuner.callbacks.delete(self)
CCSError.to_native(:CCS_SUCCESS)
rescue CCSError => e
e.to_native
end
}
askwrapper = lambda { |data, count, p_configurations, p_count|
begin
configurations, count_ret = ask.call(data, p_configurations.null? ? 0 : count)
raise CCSError, :CCS_INVALID_VALUE if !p_configurations.null? && count < count_ret
if !p_configurations.null?
configurations.each_with_index { |c, i|
err = CCS.ccs_retain_object(c.handle)
CCS.error_check(err)
p_configurations.put_pointer(i*8, c.handle)
}
(count_ret...count).each { |i| p_configurations[i].put_pointer(i*8, 0) }
end
p_count.write_uint64(count_ret) unless p_count.null?
CCSError.to_native(:CCS_SUCCESS)
rescue CCSError => e
e.to_native
end
}
tellwrapper = lambda { |data, count, p_evaluations|
begin
if count > 0
evals = count.times.collect { |i| Evaluation::from_handle(p_evaluations.get_pointer(i*8)) }
tell.call(data, evals)
end
CCSError.to_native(:CCS_SUCCESS)
rescue CCSError => e
e.to_native
end
}
get_optimumswrapper = lambda { |data, count, p_evaluations, p_count|
begin
optimums = get_optimums.call(data)
raise CCSError, :CCS_INVALID_VALUE if !p_evaluations.null? && count < optimums.size
unless p_evaluations.null?
optimums.each_with_index { |o, i|
p_evaluations.put_pointer(8*i, o.handle)
}
((optimums.size)...count).each { |i| p_evaluations.put_pointer(8*i, 0) }
end
p_count.write_uint64(optimums.size) unless p_count.null?
CCSError.to_native(:CCS_SUCCESS)
rescue CCSError => e
e.to_native
end
}
get_historywrapper = lambda { |data, count, p_evaluations, p_count|
begin
history = get_history.call(data)
raise CCSError, :CCS_INVALID_VALUE if !p_evaluations.null? && count < history.size
unless p_evaluations.null?
history.each_with_index { |e, i|
p_evaluations.put_pointer(8*i, e.handle)
}
((history.size)...count).each { |i| p_evaluations.put_pointer(8*i, 0) }
end
p_count.write_uint64(history.size) unless p_count.null?
CCSError.to_native(:CCS_SUCCESS)
rescue CCSError => e
e.to_native
end
}
vector = UserDefinedTunerVector::new
vector[:del] = delwrapper
vector[:ask] = askwrapper
vector[:tell] = tellwrapper
vector[:get_optimums] = get_optimumswrapper
vector[:get_history] = get_historywrapper
ptr = MemoryPointer::new(:ccs_tuner_t)
res = CCS.ccs_create_user_defined_tuner(name, configuration_space, objective_space, user_data, vector, tuner_data, ptr)
CCS.error_check(res)
super(ptr.read_ccs_tuner_t, retain: false)
UserDefinedTuner.callbacks[self] = [delwrapper, askwrapper, tellwrapper, get_optimumswrapper, get_historywrapper]
end
end
end
end
......@@ -7,7 +7,7 @@ class CConfigSpaceTestTuner < Minitest::Test
CCS.init
end
def test_create
def create_tuning_problem
cs = CCS::ConfigurationSpace::new(name: "cspace")
h1 = CCS::NumericalHyperparameter::new(lower: -5.0, upper: 5.0)
h2 = CCS::NumericalHyperparameter::new(lower: -5.0, upper: 5.0)
......@@ -20,6 +20,11 @@ class CConfigSpaceTestTuner < Minitest::Test
e1 = CCS::Variable::new(hyperparameter: v1)
e2 = CCS::Variable::new(hyperparameter: v2)
os.add_objectives( [e1, e2] )
[cs, os]
end
def test_create_random
cs, os = create_tuning_problem
t = CCS::RandomTuner::new(name: "tuner", configuration_space: cs, objective_space: os)
assert_equal( "tuner", t.name )
assert_equal( :CCS_TUNER_RANDOM, t.type )
......@@ -40,5 +45,68 @@ class CConfigSpaceTestTuner < Minitest::Test
objs = t.optimums.collect(&:objective_values).sort
objs.collect { |(_, v)| v }.each_cons(2) { |v1, v2| assert( (v1 <=> v2) > 0 ) }
end
def test_user_defined
history = []
optimums = []
del = lambda { |data| nil }
ask = lambda { |data, count|
if count > 0
cs = CCS::ConfigurationSpace::from_handle(data[:common_data][:configuration_space])
[cs.samples(count), count]
else
[nil, 1]
end
}
tell = lambda { |data, evaluations|
history += evaluations
evaluations.each { |e|
discard = false
optimums = optimums.collect { |o|
unless discard
case e.cmp(o)
when :CCS_EQUIVALENT, :CCS_WORSE
discard = true
o
when :CCS_NOT_COMPARABLE
o
else
nil
end
else
o
end
}.compact
optimums.push(e) unless discard
}
}
get_history = lambda { |data|
history
}
get_optimums = lambda { |data|
optimums
}
cs, os = create_tuning_problem
t = CCS::UserDefinedTuner::new(name: "tuner", configuration_space: cs, objective_space: os, del: del, ask: ask, tell: tell, get_optimums: get_optimums, get_history: get_history)
assert_equal( "tuner", t.name )
assert_equal( :CCS_TUNER_USER_DEFINED, t.type )
func = lambda { |(x, y, z)|
[(x-2)**2, Math.sin(z+y)]
}
evals = t.ask(100).collect { |c|
CCS::Evaluation::new(objective_space: os, configuration: c, values: func[c.values])
}
t.tell evals
hist = t.history
assert_equal(100, hist.size)
evals = t.ask(100).collect { |c|
CCS::Evaluation::new(objective_space: os, configuration: c, values: func[c.values])
}
t.tell evals
assert_equal(200, t.history_size)
optims = t.optimums
objs = optims.collect(&:objective_values).sort
objs.collect { |(_, v)| v }.each_cons(2) { |v1, v2| assert( (v1 <=> v2) > 0 ) }
end
end
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment