menoh 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,74 @@
1
+ require 'rmagick'
2
+ require 'menoh'
3
+
4
+ # load dataset
5
+ image_list = [
6
+ './data/0.png',
7
+ './data/1.png',
8
+ './data/2.png',
9
+ './data/3.png',
10
+ './data/4.png',
11
+ './data/5.png',
12
+ './data/6.png',
13
+ './data/7.png',
14
+ './data/8.png',
15
+ './data/9.png'
16
+ ]
17
+ input_shape = {
18
+ channel_num: 1,
19
+ width: 28,
20
+ height: 28
21
+ }
22
+
23
+ # load ONNX file
24
+ onnx_obj = Menoh::Menoh.new './data/mnist.onnx'
25
+
26
+ # onnx variable name
27
+ MNIST_IN_NAME = '139900320569040'.freeze
28
+ MNIST_OUT_NAME = '139898462888656'.freeze
29
+
30
+ # model options for model
31
+ model_opt = {
32
+ backend: 'mkldnn',
33
+ input_layers: [
34
+ {
35
+ name: MNIST_IN_NAME,
36
+ dims: [
37
+ image_list.length,
38
+ input_shape[:channel_num],
39
+ input_shape[:width],
40
+ input_shape[:height]
41
+ ]
42
+ }
43
+ ],
44
+ output_layers: [MNIST_OUT_NAME]
45
+ }
46
+ # make model for inference under 'model_opt'
47
+ model = onnx_obj.make_model model_opt
48
+
49
+ # prepare dataset
50
+ image_set = [
51
+ {
52
+ name: MNIST_IN_NAME,
53
+ data: image_list.map do |image_filepath|
54
+ image = Magick::Image.read(image_filepath).first
55
+ image = image.resize_to_fill(input_shape[:width], input_shape[:height])
56
+ image.export_pixels(0, 0, image.columns, image.rows, 'i').map { |pix| pix / 256 }
57
+ end.flatten
58
+ }
59
+ ]
60
+ # execute inference
61
+ inferenced_results = model.run image_set
62
+
63
+ categories = (0..9).to_a
64
+ TOP_K = 1
65
+ layer_result = inferenced_results.find { |x| x[:name] == MNIST_OUT_NAME }
66
+ layer_result[:data].zip(image_list).each do |image_result, image_filepath|
67
+ # sort by score
68
+ sorted_result = image_result.zip(categories).sort_by { |x| -x[0] }
69
+
70
+ # display result
71
+ sorted_result[0, TOP_K].each do |score, category|
72
+ puts "#{image_filepath} = #{category} : #{score}"
73
+ end
74
+ end
@@ -0,0 +1,75 @@
1
+ require 'rmagick'
2
+ require 'menoh'
3
+
4
+ # load dataset
5
+ image_list = [
6
+ './data/0.png',
7
+ './data/1.png',
8
+ './data/2.png',
9
+ './data/3.png',
10
+ './data/4.png',
11
+ './data/5.png',
12
+ './data/6.png',
13
+ './data/7.png',
14
+ './data/8.png',
15
+ './data/9.png'
16
+ ]
17
+ input_shape = {
18
+ channel_num: 1,
19
+ width: 28,
20
+ height: 28
21
+ }
22
+
23
+ # onnx variable name
24
+ MNIST_IN_NAME = '139900320569040'.freeze
25
+ MNIST_OUT_NAME = '139898462888656'.freeze
26
+
27
+ # model options for model
28
+ model_opt = {
29
+ backend: 'mkldnn',
30
+ input_layers: [
31
+ {
32
+ name: MNIST_IN_NAME,
33
+ dims: [
34
+ image_list.length,
35
+ input_shape[:channel_num],
36
+ input_shape[:width],
37
+ input_shape[:height]
38
+ ]
39
+ }
40
+ ],
41
+ output_layers: [MNIST_OUT_NAME]
42
+ }
43
+
44
+ # load ONNX file
45
+ Menoh::Menoh.new './data/mnist.onnx' do |onnx_obj|
46
+ # make model for inference under 'model_opt'
47
+ onnx_obj.make_model model_opt do |model|
48
+ # prepare dataset
49
+ image_set = [
50
+ {
51
+ name: MNIST_IN_NAME,
52
+ data: image_list.map do |image_filepath|
53
+ image = Magick::Image.read(image_filepath).first
54
+ image = image.resize_to_fill(input_shape[:width], input_shape[:height])
55
+ image.export_pixels(0, 0, image.columns, image.rows, 'i').map { |pix| pix / 256 }
56
+ end.flatten
57
+ }
58
+ ]
59
+ # execute inference
60
+ model.run image_set do |inferenced_results|
61
+ categories = (0..9).to_a
62
+ TOP_K = 1
63
+ layer_result = inferenced_results.find { |x| x[:name] == MNIST_OUT_NAME }
64
+ layer_result[:data].zip(image_list).each do |image_result, image_filepath|
65
+ # sort by score
66
+ sorted_result = image_result.zip(categories).sort_by { |x| -x[0] }
67
+
68
+ # display result
69
+ sorted_result[0, TOP_K].each do |score, category|
70
+ puts "#{image_filepath} = #{category} : #{score}"
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,89 @@
1
+ require 'open-uri'
2
+ require 'rmagick'
3
+ require 'menoh'
4
+ # TODO revise api
5
+ # download dependencies
6
+ def download_file(url, output)
7
+ return if File.exist? output
8
+ puts "downloading... #{url}"
9
+ File.open(output, 'wb') do |f_output|
10
+ open(url, 'rb') do |f_input|
11
+ f_output.write f_input.read
12
+ end
13
+ end
14
+ end
15
+ download_file('https://www.dropbox.com/s/bjfn9kehukpbmcm/VGG16.onnx?dl=1', './data/VGG16.onnx')
16
+ download_file('https://raw.githubusercontent.com/HoldenCaulfieldRye/caffe/master/data/ilsvrc12/synset_words.txt', './data/synset_words.txt')
17
+ download_file('https://upload.wikimedia.org/wikipedia/commons/5/54/Light_sussex_hen.jpg', './data/Light_sussex_hen.jpg')
18
+ download_file('https://upload.wikimedia.org/wikipedia/commons/f/fd/FoS20162016_0625_151036AA_%2827826100631%29.jpg', './data/honda_nsx.jpg')
19
+
20
+ # load dataset
21
+ image_list = [
22
+ './data/Light_sussex_hen.jpg',
23
+ './data/honda_nsx.jpg'
24
+ ]
25
+ input_shape = {
26
+ channel_num: 3,
27
+ width: 224,
28
+ height: 224
29
+ }
30
+
31
+ # load ONNX file
32
+ onnx_obj = Menoh::Menoh.new './data/VGG16.onnx'
33
+
34
+ # onnx variable name
35
+ CONV1_1_IN_NAME = '140326425860192'.freeze
36
+ FC6_OUT_NAME = '140326200777584'.freeze
37
+ SOFTMAX_OUT_NAME = '140326200803680'.freeze
38
+
39
+ # model options for model
40
+ model_opt = {
41
+ backend: 'mkldnn',
42
+ input_layers: [
43
+ {
44
+ name: CONV1_1_IN_NAME,
45
+ dims: [
46
+ image_list.length,
47
+ input_shape[:channel_num],
48
+ input_shape[:width],
49
+ input_shape[:height]
50
+ ]
51
+ }
52
+ ],
53
+ output_layers: [FC6_OUT_NAME, SOFTMAX_OUT_NAME]
54
+ }
55
+ # make model for inference under 'model_opt'
56
+ model = onnx_obj.make_model model_opt
57
+
58
+ # prepare dataset
59
+ image_set = [
60
+ {
61
+ name: CONV1_1_IN_NAME,
62
+ data: image_list.map do |image_filepath|
63
+ image = Magick::Image.read(image_filepath).first
64
+ image = image.resize_to_fill(input_shape[:width], input_shape[:height])
65
+ 'BGR'.split('').map do |color|
66
+ image.export_pixels(0, 0, image.columns, image.rows, color).map { |pix| pix / 256 }
67
+ end.flatten
68
+ end.flatten
69
+ }
70
+ ]
71
+
72
+ # execute inference
73
+ inferenced_results = model.run image_set
74
+
75
+ # load category definition
76
+ categories = File.read('./data/synset_words.txt').split("\n")
77
+ TOP_K = 5
78
+ layer_result = inferenced_results.find { |x| x[:name] == SOFTMAX_OUT_NAME }
79
+ layer_result[:data].zip(image_list).each do |image_result, image_filepath|
80
+ puts "=== Result for #{image_filepath} ==="
81
+
82
+ # sort by score
83
+ sorted_result = image_result.zip(categories).sort_by { |x| -x[0] }
84
+
85
+ # display result
86
+ sorted_result[0, TOP_K].each do |score, category|
87
+ puts "#{category} : #{score}"
88
+ end
89
+ end
@@ -0,0 +1,14 @@
1
+ require 'mkmf'
2
+
3
+ # have_library("stdc++")
4
+ have_library('mkldnn')
5
+ have_library('protobuf')
6
+
7
+ menoh_dir = dir_config('menoh')
8
+ $INCFLAGS << " -I#{menoh_dir[0]}/menoh"
9
+ have_library('menoh')
10
+
11
+ # $CPPFLAGS << " -std=c++14"
12
+ $DLDFLAGS << ' -rdynamic'
13
+
14
+ create_makefile('menoh/menoh_native')
@@ -0,0 +1,328 @@
1
+ #include "menoh_ruby.h"
2
+
3
+ #define ERROR_CHECK(statement, exceptiontype) \
4
+ { \
5
+ menoh_error_code ec = statement; \
6
+ if (ec) { \
7
+ rb_raise(exceptiontype, "%s", menoh_get_last_error_message()); \
8
+ return Qnil; \
9
+ } \
10
+ }
11
+
12
+ typedef struct menoh_ruby {
13
+ menoh_model_data_handle model_data;
14
+ } menoh_ruby;
15
+
16
+ static menoh_ruby *getONNX(VALUE self) {
17
+ menoh_ruby *p;
18
+ Data_Get_Struct(self, menoh_ruby, p);
19
+ return p;
20
+ }
21
+
22
+ static void wrap_menoh_free(menoh_ruby *p) {
23
+ if (p) {
24
+ if (p->model_data) menoh_delete_model_data(p->model_data);
25
+ ruby_xfree(p);
26
+ }
27
+ }
28
+
29
+ static VALUE wrap_menoh_alloc(VALUE klass) {
30
+ void *p = ruby_xmalloc(sizeof(menoh_ruby));
31
+ memset(p, 0, sizeof(menoh_ruby));
32
+ return Data_Wrap_Struct(klass, NULL, wrap_menoh_free, p);
33
+ }
34
+
35
+ static VALUE wrap_menoh_init(VALUE self, VALUE vfilename) {
36
+ menoh_error_code ec = menoh_error_code_success;
37
+ FilePathValue(vfilename);
38
+ char *filename = StringValueCStr(vfilename);
39
+
40
+ // Load ONNX model
41
+ menoh_model_data_handle model_data;
42
+ ERROR_CHECK(menoh_make_model_data_from_onnx(filename, &model_data),
43
+ rb_eArgError);
44
+ getONNX(self)->model_data = model_data;
45
+
46
+ return Qnil;
47
+ }
48
+
49
+ typedef struct menohModel {
50
+ menoh_model_data_handle model_data;
51
+ VALUE vbackend;
52
+ float **input_buffs;
53
+ float **output_buffs;
54
+ menoh_variable_profile_table_builder_handle vpt_builder;
55
+ menoh_variable_profile_table_handle variable_profile_table;
56
+ menoh_model_builder_handle model_builder;
57
+ menoh_model_handle model;
58
+ VALUE vinput_layers;
59
+ VALUE voutput_layers;
60
+ int32_t input_layer_num;
61
+ } menohModel;
62
+
63
+ static menohModel *getModel(VALUE self) {
64
+ menohModel *p;
65
+ Data_Get_Struct(self, menohModel, p);
66
+ return p;
67
+ }
68
+
69
+ static void wrap_model_free(menohModel *p) {
70
+ if (p) {
71
+ if (p->variable_profile_table)
72
+ menoh_delete_variable_profile_table(p->variable_profile_table);
73
+ if (p->vpt_builder)
74
+ menoh_delete_variable_profile_table_builder(p->vpt_builder);
75
+ if (p->model) menoh_delete_model(p->model);
76
+ if (p->model_builder) menoh_delete_model_builder(p->model_builder);
77
+ if (p->input_buffs) {
78
+ for (int32_t i = 0; i < p->input_layer_num; i++) {
79
+ if (p->input_buffs[i]) ruby_xfree(p->input_buffs[i]);
80
+ }
81
+ ruby_xfree(p->input_buffs);
82
+ }
83
+ if (p->output_buffs) ruby_xfree(p->output_buffs);
84
+ ruby_xfree(p);
85
+ }
86
+ }
87
+
88
+ static void wrap_model_mark(menohModel *p) {
89
+ if (p) {
90
+ if (p->vbackend) rb_gc_mark(p->vbackend);
91
+ if (p->vinput_layers) rb_gc_mark(p->vinput_layers);
92
+ if (p->voutput_layers) rb_gc_mark(p->voutput_layers);
93
+ }
94
+ }
95
+
96
+ static VALUE wrap_model_alloc(VALUE klass) {
97
+ void *p = ruby_xmalloc(sizeof(menohModel));
98
+ memset(p, 0, sizeof(menohModel));
99
+ return Data_Wrap_Struct(klass, wrap_model_mark, wrap_model_free, p);
100
+ }
101
+
102
+ static VALUE wrap_model_init(VALUE self, VALUE vonnx, VALUE option) {
103
+ // option
104
+ getModel(self)->model_data = getONNX(vonnx)->model_data;
105
+ VALUE vbackend = rb_hash_aref(option, rb_to_symbol(rb_str_new2("backend")));
106
+ getModel(self)->vbackend = vbackend;
107
+
108
+ // option
109
+ VALUE vinput_layers =
110
+ rb_hash_aref(option, rb_to_symbol(rb_str_new2("input_layers")));
111
+ VALUE voutput_layers =
112
+ rb_hash_aref(option, rb_to_symbol(rb_str_new2("output_layers")));
113
+
114
+ getModel(self)->vinput_layers = vinput_layers;
115
+ getModel(self)->voutput_layers = voutput_layers;
116
+
117
+ // get vpt builder
118
+ ERROR_CHECK(
119
+ menoh_make_variable_profile_table_builder(&(getModel(self)->vpt_builder)),
120
+ rb_eStandardError);
121
+
122
+ // set output_layer
123
+ int32_t output_layer_num =
124
+ NUM2INT(rb_funcall(voutput_layers, rb_intern("length"), 0, NULL));
125
+ for (int32_t i = 0; i < output_layer_num; i++) {
126
+ VALUE voutput_layer = rb_ary_entry(voutput_layers, i);
127
+ ERROR_CHECK(menoh_variable_profile_table_builder_add_output_profile(
128
+ getModel(self)->vpt_builder, StringValueCStr(voutput_layer),
129
+ menoh_dtype_float),
130
+ rb_eStandardError);
131
+ }
132
+
133
+ // set input layer
134
+ int32_t input_layer_num =
135
+ NUM2INT(rb_funcall(vinput_layers, rb_intern("length"), 0, NULL));
136
+ getModel(self)->input_layer_num = input_layer_num;
137
+ for (int32_t i = 0; i < input_layer_num; i++) {
138
+ VALUE vinput_layer = rb_ary_entry(vinput_layers, i);
139
+ VALUE vname = rb_hash_aref(vinput_layer, rb_to_symbol(rb_str_new2("name")));
140
+ VALUE vdims = rb_hash_aref(vinput_layer, rb_to_symbol(rb_str_new2("dims")));
141
+ int32_t dims_length =
142
+ NUM2INT(rb_funcall(vdims, rb_intern("length"), 0, NULL));
143
+
144
+ switch (dims_length) {
145
+ case 2:
146
+ ERROR_CHECK(
147
+ menoh_variable_profile_table_builder_add_input_profile_dims_2(
148
+ getModel(self)->vpt_builder, StringValueCStr(vname),
149
+ menoh_dtype_float, NUM2INT(rb_ary_entry(vdims, 0)),
150
+ NUM2INT(rb_ary_entry(vdims, 1))),
151
+ rb_eStandardError);
152
+ break;
153
+ case 4:
154
+ ERROR_CHECK(
155
+ menoh_variable_profile_table_builder_add_input_profile_dims_4(
156
+ getModel(self)->vpt_builder, StringValueCStr(vname),
157
+ menoh_dtype_float, NUM2INT(rb_ary_entry(vdims, 0)),
158
+ NUM2INT(rb_ary_entry(vdims, 1)),
159
+ NUM2INT(rb_ary_entry(vdims, 2)),
160
+ NUM2INT(rb_ary_entry(vdims, 3))),
161
+ rb_eStandardError);
162
+ break;
163
+ default:
164
+ rb_raise(rb_eStandardError, "invalid dimension length");
165
+ return Qnil;
166
+ }
167
+
168
+ // build variable provile table
169
+ ERROR_CHECK(menoh_build_variable_profile_table(
170
+ getModel(self)->vpt_builder, getModel(self)->model_data,
171
+ &(getModel(self)->variable_profile_table)),
172
+ rb_eStandardError);
173
+
174
+ // optimize
175
+ ERROR_CHECK(
176
+ menoh_model_data_optimize(getModel(self)->model_data,
177
+ getModel(self)->variable_profile_table),
178
+ rb_eStandardError);
179
+
180
+ // get model buildler
181
+ ERROR_CHECK(menoh_make_model_builder(getModel(self)->variable_profile_table,
182
+ &(getModel(self)->model_builder)),
183
+ rb_eStandardError);
184
+
185
+ // attach input buffer to model builder
186
+ getModel(self)->input_buffs =
187
+ (float **)ruby_xmalloc(sizeof(float **) * input_layer_num);
188
+ for (int32_t i = 0; i < input_layer_num; i++) {
189
+ VALUE vinput_layer = rb_ary_entry(vinput_layers, i);
190
+ VALUE vname =
191
+ rb_hash_aref(vinput_layer, rb_to_symbol(rb_str_new2("name")));
192
+ VALUE vdims =
193
+ rb_hash_aref(vinput_layer, rb_to_symbol(rb_str_new2("dims")));
194
+ int32_t dims_length =
195
+ NUM2INT(rb_funcall(vdims, rb_intern("length"), 0, NULL));
196
+
197
+ // prepare input buffer
198
+ int32_t buffer_length = 1;
199
+ for (int32_t j = 0; j < dims_length; j++)
200
+ buffer_length *= NUM2INT(rb_ary_entry(vdims, j));
201
+
202
+ float *input_buff = (float *)ruby_xmalloc(sizeof(float) * buffer_length);
203
+ getModel(self)->input_buffs[i] = input_buff;
204
+ ERROR_CHECK(
205
+ menoh_model_builder_attach_external_buffer(
206
+ getModel(self)->model_builder, StringValueCStr(vname), input_buff),
207
+ rb_eStandardError);
208
+ }
209
+
210
+ // build model
211
+ ERROR_CHECK(menoh_build_model(
212
+ getModel(self)->model_builder, getModel(self)->model_data,
213
+ StringValueCStr(vbackend), "", &(getModel(self)->model)),
214
+ rb_eStandardError);
215
+
216
+ return Qnil;
217
+ }
218
+ }
219
+
220
+ static VALUE wrap_model_run(VALUE self, VALUE dataset) {
221
+ VALUE vbackend = getModel(self)->vbackend;
222
+ VALUE vinput_layers = getModel(self)->vinput_layers;
223
+ VALUE voutput_layers = getModel(self)->voutput_layers;
224
+
225
+ int32_t input_layer_num =
226
+ NUM2INT(rb_funcall(vinput_layers, rb_intern("length"), 0, NULL));
227
+ int32_t output_layer_num =
228
+ NUM2INT(rb_funcall(voutput_layers, rb_intern("length"), 0, NULL));
229
+
230
+ // Copy input image data to model's input array
231
+ for (int32_t i = 0; i < input_layer_num; i++) {
232
+ VALUE vinput_layer = rb_ary_entry(vinput_layers, i);
233
+ VALUE vname = rb_hash_aref(vinput_layer, rb_to_symbol(rb_str_new2("name")));
234
+ VALUE vdims = rb_hash_aref(vinput_layer, rb_to_symbol(rb_str_new2("dims")));
235
+ int32_t dims_length =
236
+ NUM2INT(rb_funcall(vdims, rb_intern("length"), 0, NULL));
237
+ int32_t buffer_length = 1;
238
+ for (int32_t j = 0; j < dims_length; j++)
239
+ buffer_length *= NUM2INT(rb_ary_entry(vdims, j));
240
+
241
+ VALUE data = rb_ary_entry(dataset, i);
242
+ for (int32_t j = 0; j < buffer_length; j++) {
243
+ getModel(self)->input_buffs[i][j] =
244
+ (float)(NUM2DBL(rb_ary_entry(data, j)));
245
+ }
246
+ }
247
+
248
+ // attach output buffer to model
249
+ getModel(self)->output_buffs =
250
+ (float **)ruby_xmalloc(sizeof(float *) * output_layer_num);
251
+ for (int32_t i = 0; i < output_layer_num; i++) {
252
+ VALUE voutput_layer = rb_ary_entry(voutput_layers, i);
253
+ float *output_buff;
254
+ ERROR_CHECK(menoh_model_get_variable_buffer_handle(
255
+ getModel(self)->model, StringValueCStr(voutput_layer),
256
+ (void **)&output_buff),
257
+ rb_eStandardError);
258
+ getModel(self)->output_buffs[i] = output_buff;
259
+ }
260
+
261
+ // run model
262
+ ERROR_CHECK(menoh_model_run(getModel(self)->model), rb_eStandardError);
263
+
264
+ // Get output
265
+ VALUE results = rb_ary_new();
266
+ for (int32_t output_layer_i = 0; output_layer_i < output_layer_num;
267
+ output_layer_i++) {
268
+ VALUE voutput_layer = rb_ary_entry(voutput_layers, output_layer_i);
269
+ VALUE result_each = rb_hash_new();
270
+
271
+ // get dimention of output layers
272
+ int32_t dim_size;
273
+ int32_t output_buffer_length = 1;
274
+ ERROR_CHECK(menoh_variable_profile_table_get_dims_size(
275
+ getModel(self)->variable_profile_table,
276
+ StringValueCStr(voutput_layer), &(dim_size)),
277
+ rb_eStandardError);
278
+ VALUE vresult_shape = rb_ary_new();
279
+ // get each size of dimention
280
+ for (int32_t dim = 0; dim < dim_size; dim++) {
281
+ int32_t size;
282
+ ERROR_CHECK(menoh_variable_profile_table_get_dims_at(
283
+ getModel(self)->variable_profile_table,
284
+ StringValueCStr(voutput_layer), dim, &(size)),
285
+ rb_eStandardError);
286
+ rb_ary_push(vresult_shape, INT2NUM(size));
287
+ output_buffer_length *= size;
288
+ }
289
+
290
+ // Convert result to Ruby Array
291
+ VALUE vresult_buffer = rb_ary_new();
292
+ for (int32_t j = 0; j < output_buffer_length; j++) {
293
+ float *output_buff;
294
+ output_buff = getModel(self)->output_buffs[output_layer_i];
295
+ rb_ary_push(vresult_buffer, DBL2NUM(*(output_buff + j)));
296
+ }
297
+
298
+ rb_hash_aset(result_each, rb_to_symbol(rb_str_new2("name")), voutput_layer);
299
+ rb_hash_aset(result_each, rb_to_symbol(rb_str_new2("shape")),
300
+ vresult_shape);
301
+ rb_hash_aset(result_each, rb_to_symbol(rb_str_new2("data")),
302
+ vresult_buffer);
303
+ rb_ary_push(results, result_each);
304
+ }
305
+
306
+ return results;
307
+ }
308
+
309
+ VALUE mMenoh;
310
+
311
+ void Init_menoh_native() {
312
+ mMenoh = rb_define_module("Menoh");
313
+
314
+ VALUE onnx = rb_define_class_under(mMenoh, "Menoh", rb_cObject);
315
+
316
+ rb_define_alloc_func(onnx, wrap_menoh_alloc);
317
+ rb_define_private_method(onnx, "native_init",
318
+ RUBY_METHOD_FUNC(wrap_menoh_init), 1);
319
+
320
+ VALUE model = rb_define_class_under(mMenoh, "MenohModel", rb_cObject);
321
+
322
+ rb_define_alloc_func(model, wrap_model_alloc);
323
+ rb_define_private_method(model, "native_init",
324
+ RUBY_METHOD_FUNC(wrap_model_init), 2);
325
+
326
+ rb_define_private_method(model, "native_run",
327
+ RUBY_METHOD_FUNC(wrap_model_run), 1);
328
+ }