1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *    http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "jni/jni_stochastic_linear_ranker.h"
18#include "native/common_defs.h"
19#include "native/sparse_weight_vector.h"
20#include "native/stochastic_linear_ranker.h"
21
22#include <vector>
23#include <string>
24using std::string;
25using std::vector;
26using std::hash_map;
27using learning_stochastic_linear::StochasticLinearRanker;
28using learning_stochastic_linear::SparseWeightVector;
29
30void CreateSparseWeightVector(JNIEnv* env, const jobjectArray keys, const float* values,
31    const int length, SparseWeightVector<string> * sample) {
32
33  for (int i = 0; i < length; ++i) {
34    jboolean iscopy;
35    jstring s = (jstring) env->GetObjectArrayElement(keys, i);
36    const char *key = env->GetStringUTFChars(s, &iscopy);
37    sample->SetElement(key, static_cast<double>(values[i]));
38    env->ReleaseStringUTFChars(s,key);
39  }
40}
41
42void ConvertParameter2Object(JNIEnv* env, jobjectArray *keys, jobjectArray *values,
43    const char * name , const char * paramValue, int index) {
44
45    jstring jstrK = env->NewStringUTF(name);
46    jstring jstrV = env->NewStringUTF(paramValue);
47    env->SetObjectArrayElement(*keys, index, jstrK);
48    env->SetObjectArrayElement(*values, index, jstrV);
49}
50
51void DecomposeSparseWeightVector(JNIEnv* env, jobjectArray *keys, jfloatArray *values,
52    const int length, SparseWeightVector<string> *sample) {
53
54  SparseWeightVector<string>::Wmap w_ = sample->GetMap();
55  int i=0;
56  for ( SparseWeightVector<string>::Witer_const iter = w_.begin();
57    iter != w_.end(); ++iter) {
58    std::string key = iter->first;
59    float value = (float) iter->second;
60    jstring jstr = env->NewStringUTF(key.c_str());
61    env->SetObjectArrayElement(*keys, i, jstr);
62    jfloat s[1];
63    s[0] = value;
64    env->SetFloatArrayRegion(*values, i, 1, s);
65    i++;
66  }
67}
68
69jboolean Java_android_bordeaux_learning_StochasticLinearRanker_nativeSetWeightClassifier(
70    JNIEnv* env,
71    jobject thiz,
72    jobjectArray key_array_model,
73    jfloatArray value_array_model,
74    jfloat normalizer_model,
75    jint paPtr) {
76
77  StochasticLinearRanker<string>* classifier = (StochasticLinearRanker<string>*) paPtr;
78  if (classifier && key_array_model && value_array_model && normalizer_model) {
79    const int keys_m_len = env->GetArrayLength(key_array_model);
80    jfloat* values_m = env->GetFloatArrayElements(value_array_model, NULL);
81    const int values_m_len = env->GetArrayLength(value_array_model);
82
83    if (values_m && key_array_model && values_m_len == keys_m_len) {
84      SparseWeightVector<string> model;
85      CreateSparseWeightVector(env, key_array_model, values_m, values_m_len, &model);
86      model.SetNormalizer(normalizer_model);
87      classifier->LoadWeights(model);
88      env->ReleaseFloatArrayElements(value_array_model, values_m, JNI_ABORT);
89      return JNI_TRUE;
90    }
91  }
92  return JNI_FALSE;
93}
94
95jboolean Java_android_bordeaux_learning_StochasticLinearRanker_nativeSetParameterClassifier(
96    JNIEnv* env,
97    jobject thiz,
98    jstring key,
99    jstring value,
100    jint paPtr) {
101
102  StochasticLinearRanker<string>* classifier = (StochasticLinearRanker<string>*) paPtr;
103  jboolean iscopy;
104  const char *cKey = env->GetStringUTFChars(key, &iscopy);
105  const char *cValue = env->GetStringUTFChars(value, &iscopy);
106  float v;
107  if (strcmp(cKey, ITR_NUM) == 0){
108    sscanf(cValue, "%f", &v);
109    classifier->SetIterationNumber((uint64) v);
110    return JNI_TRUE;
111  }
112  else if (strcmp(cKey, NORM_CONSTRAINT) == 0){
113    sscanf(cValue, "%f", &v);
114    classifier->SetNormConstraint((double) v);
115    return JNI_TRUE;
116  }
117  else if (strcmp(cKey, REG_TYPE) == 0){
118    if (strcmp(cValue, REG_TYPE_L0 ) == 0)
119      classifier->SetRegularizationType(learning_stochastic_linear::L0);
120    else if (strcmp(cValue, REG_TYPE_L1 ) == 0)
121      classifier->SetRegularizationType(learning_stochastic_linear::L1);
122    else if (strcmp(cValue, REG_TYPE_L2 ) == 0)
123      classifier->SetRegularizationType(learning_stochastic_linear::L2);
124    else if (strcmp(cValue, REG_TYPE_L1L2 ) == 0)
125      classifier->SetRegularizationType(learning_stochastic_linear::L1L2);
126    else if (strcmp(cValue, REG_TYPE_L1LInf ) == 0)
127      classifier->SetRegularizationType(learning_stochastic_linear::L1LInf);
128    else {
129      ALOGE("Error: %s is not a Regularization Type", cValue);
130      return JNI_FALSE;
131    }
132    return JNI_TRUE;
133  }
134  else if (strcmp(cKey, LAMBDA) == 0){
135    sscanf(cValue, "%f", &v);
136    classifier->SetLambda((double) v);
137    return JNI_TRUE;
138  }
139  else if (strcmp(cKey, UPDATE_TYPE) == 0){
140    if (strcmp(cValue, UPDATE_TYPE_FULL_CS) == 0)
141      classifier->SetUpdateType(learning_stochastic_linear::FULL_CS);
142    else if (strcmp(cValue, UPDATE_TYPE_CLIP_CS) == 0)
143      classifier->SetUpdateType(learning_stochastic_linear::CLIP_CS);
144    else if (strcmp(cValue, UPDATE_TYPE_REG_CS ) == 0)
145      classifier->SetUpdateType(learning_stochastic_linear::REG_CS);
146    else if (strcmp(cValue, UPDATE_TYPE_SL) == 0)
147      classifier->SetUpdateType(learning_stochastic_linear::SL);
148    else if (strcmp(cValue, UPDATE_TYPE_ADAPTIVE_REG) == 0)
149      classifier->SetUpdateType(learning_stochastic_linear::ADAPTIVE_REG);
150    else {
151      ALOGE("Error: %s is not an Update Type", cValue);
152      return JNI_FALSE;
153    }
154    return JNI_TRUE;
155  }
156  else if (strcmp(cKey, ADAPT_MODE) == 0){
157    if (strcmp(cValue, ADAPT_MODE_CONST ) == 0)
158      classifier->SetAdaptationMode(learning_stochastic_linear::CONST);
159    else if (strcmp(cValue, ADAPT_MODE_INV_LINEAR ) == 0)
160      classifier->SetAdaptationMode(learning_stochastic_linear::INV_LINEAR);
161    else if (strcmp(cValue, ADAPT_MODE_INV_QUADRATIC ) == 0)
162      classifier->SetAdaptationMode(learning_stochastic_linear::INV_QUADRATIC);
163    else if (strcmp(cValue, ADAPT_MODE_INV_SQRT ) == 0)
164      classifier->SetAdaptationMode(learning_stochastic_linear::INV_SQRT);
165    else {
166      ALOGE("Error: %s is not an Adaptation Mode", cValue);
167      return JNI_FALSE;
168    }
169    return JNI_TRUE;
170  }
171  else if (strcmp(cKey, KERNEL_TYPE) == 0){
172    if (strcmp(cValue, KERNEL_TYPE_LINEAR ) == 0)
173      classifier->SetKernelType(learning_stochastic_linear::LINEAR);
174    else if (strcmp(cValue, KERNEL_TYPE_POLY ) == 0)
175      classifier->SetKernelType(learning_stochastic_linear::POLY);
176    else if (strcmp(cValue, KERNEL_TYPE_RBF ) == 0)
177      classifier->SetKernelType(learning_stochastic_linear::RBF);
178    else {
179      ALOGE("Error: %s is not a Kernel Type", cValue);
180      return JNI_FALSE;
181    }
182    return JNI_TRUE;
183  }
184  else if (strcmp(cKey, KERNEL_PARAM) == 0){
185    sscanf(cValue, "%f", &v);
186    classifier->SetKernelParam((double) v);
187    return JNI_TRUE;
188  }
189  else if (strcmp(cKey, KERNEL_GAIN) == 0){
190    sscanf(cValue, "%f", &v);
191    classifier->SetKernelGain((double) v);
192    return JNI_TRUE;
193  }
194  else if (strcmp(cKey, KERNEL_BIAS) == 0){
195    sscanf(cValue, "%f", &v);
196    classifier->SetKernelBias((double) v);
197    return JNI_TRUE;
198  }
199  else if (strcmp(cKey, LOSS_TYPE) == 0){
200    if (strcmp(cValue, LOSS_TYPE_PAIRWISE ) == 0)
201      classifier->SetRankLossType(learning_stochastic_linear::PAIRWISE);
202    else if (strcmp(cValue, LOSS_TYPE_RECIPROCAL_RANK ) == 0)
203      classifier->SetRankLossType(learning_stochastic_linear::RECIPROCAL_RANK);
204    else {
205      ALOGE("Error: %s is not a Kernel Type", cValue);
206      return JNI_FALSE;
207    }
208    return JNI_TRUE;
209  }
210  else if (strcmp(cKey, ACC_PROB) == 0){
211    sscanf(cValue, "%f", &v);
212    classifier->SetAcceptanceProbability((double) v);
213    return JNI_TRUE;
214  }
215  else if (strcmp(cKey, MIN_BATCH_SIZE) == 0){
216    sscanf(cValue, "%f", &v);
217    classifier->SetMiniBatchSize((uint64) v);
218    return JNI_TRUE;
219  }
220  else if (strcmp(cKey, GRAD_L0_NORM) == 0){
221    sscanf(cValue, "%f", &v);
222    classifier->SetGradientL0Norm((int32) v);
223    return JNI_TRUE;
224  }
225  ALOGE("Error: %s is not a ranker parameter", cKey);
226  return JNI_FALSE;
227}
228
229jint Java_android_bordeaux_learning_StochasticLinearRanker_nativeGetLengthClassifier(
230  JNIEnv* env,
231  jobject thiz,
232  jint paPtr) {
233
234  StochasticLinearRanker<string>* classifier = (StochasticLinearRanker<string>*) paPtr;
235  SparseWeightVector<string> M_weights;
236  classifier->SaveWeights(&M_weights);
237
238  SparseWeightVector<string>::Wmap w_map = M_weights.GetMap();
239  int len = w_map.size();
240  return len;
241}
242
243std::string ConvertFloat2String(float v){
244    std::stringstream converter;
245    converter << v;
246    return converter.str();
247}
248
249void Java_android_bordeaux_learning_StochasticLinearRanker_nativeGetParameterClassifier(
250    JNIEnv* env,
251    jobject thiz,
252    jobjectArray key_array_param,
253    jobjectArray value_array_param,
254    jint paPtr){
255
256  std::string s;
257  StochasticLinearRanker<string>* classifier = (StochasticLinearRanker<string>*) paPtr;
258  s = ConvertFloat2String((float) classifier->GetIterationNumber());
259  ConvertParameter2Object(env, &key_array_param, &value_array_param, ITR_NUM, s.c_str(), 0 );
260
261  s = ConvertFloat2String((float) classifier->GetNormContraint());
262  ConvertParameter2Object(env, &key_array_param, &value_array_param, NORM_CONSTRAINT, s.c_str(), 1 );
263
264  float value = (float) classifier->GetRegularizationType();
265  switch ((int) value) {
266    case learning_stochastic_linear::L0 :
267      s = REG_TYPE_L0;
268      break;
269    case learning_stochastic_linear::L1 :
270      s = REG_TYPE_L1;
271      break;
272    case learning_stochastic_linear::L2 :
273      s = REG_TYPE_L2;
274      break;
275    case learning_stochastic_linear::L1L2 :
276      s = REG_TYPE_L1L2;
277      break;
278    case learning_stochastic_linear::L1LInf :
279      s = REG_TYPE_L1LInf;
280      break;
281  }
282  ConvertParameter2Object(env, &key_array_param, &value_array_param, REG_TYPE, s.c_str(), 2 );
283
284  s = ConvertFloat2String((float) classifier->GetLambda());
285  ConvertParameter2Object(env, &key_array_param, &value_array_param, LAMBDA, s.c_str(), 3 );
286
287  value = (float) classifier->GetUpdateType();
288  switch ((int) value) {
289    case learning_stochastic_linear::FULL_CS :
290      s = UPDATE_TYPE_FULL_CS;
291      break;
292    case learning_stochastic_linear::CLIP_CS :
293      s = UPDATE_TYPE_CLIP_CS;
294      break;
295    case learning_stochastic_linear::REG_CS :
296      s = UPDATE_TYPE_REG_CS;
297      break;
298    case learning_stochastic_linear::SL :
299      s = UPDATE_TYPE_SL;
300      break;
301    case learning_stochastic_linear::ADAPTIVE_REG :
302      s = UPDATE_TYPE_ADAPTIVE_REG;
303      break;
304  }
305  ConvertParameter2Object(env, &key_array_param, &value_array_param, UPDATE_TYPE, s.c_str(), 4 );
306
307  value = (float) classifier->GetAdaptationMode();
308  switch ((int) value) {
309    case learning_stochastic_linear::CONST :
310      s = ADAPT_MODE_CONST;
311      break;
312    case learning_stochastic_linear::INV_LINEAR :
313      s = ADAPT_MODE_INV_LINEAR;
314      break;
315    case learning_stochastic_linear::INV_QUADRATIC :
316      s = ADAPT_MODE_INV_QUADRATIC;
317      break;
318    case learning_stochastic_linear::INV_SQRT :
319      s = ADAPT_MODE_INV_SQRT;
320      break;
321  }
322  ConvertParameter2Object(env, &key_array_param, &value_array_param, ADAPT_MODE, s.c_str(), 5 );
323
324  value = (float) classifier->GetKernelType();
325  switch ((int) value) {
326    case learning_stochastic_linear::LINEAR :
327      s = KERNEL_TYPE_LINEAR;
328      break;
329    case learning_stochastic_linear::POLY :
330      s = KERNEL_TYPE_POLY;
331      break;
332    case learning_stochastic_linear::RBF :
333      s = KERNEL_TYPE_RBF;
334      break;
335  }
336  ConvertParameter2Object(env, &key_array_param, &value_array_param, KERNEL_TYPE, s.c_str(), 6 );
337
338  s = ConvertFloat2String((float) classifier->GetKernelParam());
339  ConvertParameter2Object(env, &key_array_param, &value_array_param, KERNEL_PARAM, s.c_str(), 7 );
340
341  s = ConvertFloat2String((float) classifier->GetKernelGain());
342  ConvertParameter2Object(env, &key_array_param, &value_array_param, KERNEL_GAIN, s.c_str(), 8 );
343
344  s = ConvertFloat2String((float)classifier->GetKernelBias());
345  ConvertParameter2Object(env, &key_array_param, &value_array_param, KERNEL_BIAS, s.c_str(), 9 );
346
347  value = (float) classifier->GetRankLossType();
348  switch ((int) value) {
349    case learning_stochastic_linear::PAIRWISE :
350      s = LOSS_TYPE_PAIRWISE;
351      break;
352    case learning_stochastic_linear::RECIPROCAL_RANK :
353      s = LOSS_TYPE_RECIPROCAL_RANK;
354      break;
355  }
356  ConvertParameter2Object(env, &key_array_param, &value_array_param, LOSS_TYPE, s.c_str(), 10 );
357
358  s = ConvertFloat2String((float) classifier->GetAcceptanceProbability());
359  ConvertParameter2Object(env, &key_array_param, &value_array_param, ACC_PROB, s.c_str(), 11 );
360
361  s = ConvertFloat2String((float) classifier->GetMiniBatchSize());
362  ConvertParameter2Object(env, &key_array_param, &value_array_param, MIN_BATCH_SIZE, s.c_str(), 12 );
363
364  s = ConvertFloat2String((float) classifier->GetGradientL0Norm());
365  ConvertParameter2Object(env, &key_array_param, &value_array_param, GRAD_L0_NORM, s.c_str(), 13 );
366}
367
368void Java_android_bordeaux_learning_StochasticLinearRanker_nativeGetWeightClassifier(
369  JNIEnv* env,
370  jobject thiz,
371  jobjectArray key_array_model,
372  jfloatArray value_array_model,
373  jfloat normalizer,
374  jint paPtr) {
375
376  StochasticLinearRanker<string>* classifier = (StochasticLinearRanker<string>*) paPtr;
377  SparseWeightVector<string> M_weights;
378  classifier->SaveWeights(&M_weights);
379  SparseWeightVector<string>::Wmap w_map = M_weights.GetMap();
380  int array_len = w_map.size();
381
382  normalizer = M_weights.GetNormalizer();
383  DecomposeSparseWeightVector(env, &key_array_model, &value_array_model, array_len, &M_weights);
384}
385
386jint Java_android_bordeaux_learning_StochasticLinearRanker_initNativeClassifier(JNIEnv* env,
387                             jobject thiz) {
388  StochasticLinearRanker<string>* classifier = new StochasticLinearRanker<string>();
389  return ((jint) classifier);
390}
391
392jboolean Java_android_bordeaux_learning_StochasticLinearRanker_deleteNativeClassifier(JNIEnv* env,
393                               jobject thiz,
394                               jint paPtr) {
395  StochasticLinearRanker<string>* classifier = (StochasticLinearRanker<string>*) paPtr;
396  delete classifier;
397  return JNI_TRUE;
398}
399
400jboolean Java_android_bordeaux_learning_StochasticLinearRanker_nativeUpdateClassifier(
401  JNIEnv* env,
402  jobject thiz,
403  jobjectArray key_array_positive,
404  jfloatArray value_array_positive,
405  jobjectArray key_array_negative,
406  jfloatArray value_array_negative,
407  jint paPtr) {
408  StochasticLinearRanker<string>* classifier = (StochasticLinearRanker<string>*) paPtr;
409
410  if (classifier && key_array_positive && value_array_positive &&
411      key_array_negative && value_array_negative) {
412
413    const int keys_p_len = env->GetArrayLength(key_array_positive);
414    jfloat* values_p = env->GetFloatArrayElements(value_array_positive, NULL);
415    const int values_p_len = env->GetArrayLength(value_array_positive);
416    jfloat* values_n = env->GetFloatArrayElements(value_array_negative, NULL);
417    const int values_n_len = env->GetArrayLength(value_array_negative);
418    const int keys_n_len = env->GetArrayLength(key_array_negative);
419
420    if (values_p && key_array_positive && values_p_len == keys_p_len &&
421      values_n && key_array_negative && values_n_len == keys_n_len) {
422
423      SparseWeightVector<string> sample_pos;
424      SparseWeightVector<string> sample_neg;
425      CreateSparseWeightVector(env, key_array_positive, values_p, values_p_len, &sample_pos);
426      CreateSparseWeightVector(env, key_array_negative, values_n, values_n_len, &sample_neg);
427      classifier->UpdateClassifier(sample_pos, sample_neg);
428      env->ReleaseFloatArrayElements(value_array_negative, values_n, JNI_ABORT);
429      env->ReleaseFloatArrayElements(value_array_positive, values_p, JNI_ABORT);
430
431      return JNI_TRUE;
432    }
433    env->ReleaseFloatArrayElements(value_array_negative, values_n, JNI_ABORT);
434    env->ReleaseFloatArrayElements(value_array_positive, values_p, JNI_ABORT);
435  }
436  return JNI_FALSE;
437}
438
439jfloat Java_android_bordeaux_learning_StochasticLinearRanker_nativeScoreSample(
440  JNIEnv* env,
441  jobject thiz,
442  jobjectArray key_array,
443  jfloatArray value_array,
444  jint paPtr) {
445
446  StochasticLinearRanker<string>* classifier = (StochasticLinearRanker<string>*) paPtr;
447
448  if (classifier && key_array && value_array) {
449
450    jfloat* values = env->GetFloatArrayElements(value_array, NULL);
451    const int values_len = env->GetArrayLength(value_array);
452    const int keys_len = env->GetArrayLength(key_array);
453
454    if (values && key_array && values_len == keys_len) {
455      SparseWeightVector<string> sample;
456      CreateSparseWeightVector(env, key_array, values, values_len, &sample);
457      env->ReleaseFloatArrayElements(value_array, values, JNI_ABORT);
458      return classifier->ScoreSample(sample);
459    }
460  }
461  return -1;
462}
463