00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 #include "lib/common.h"
00013 #include "lib/io.h"
00014 #include "lib/Signal.h"
00015 #include "base/Parallel.h"
00016
00017 #include "kernel/Kernel.h"
00018 #include "kernel/CombinedKernel.h"
00019 #include "kernel/CustomKernel.h"
00020 #include "features/CombinedFeatures.h"
00021
00022 #include <string.h>
00023
00024 #ifndef WIN32
00025 #include <pthread.h>
00026 #endif
00027
00028 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00029 struct S_THREAD_PARAM
00030 {
00031 CKernel* kernel;
00032 float64_t* result;
00033 int32_t* vec_idx;
00034 int32_t start;
00035 int32_t end;
00037 float64_t* weights;
00038 int32_t* IDX;
00039 int32_t num_suppvec;
00040 };
00041 #endif // DOXYGEN_SHOULD_SKIP_THIS
00042
00043 CCombinedKernel::CCombinedKernel(int32_t size, bool asw)
00044 : CKernel(size), sv_count(0), sv_idx(NULL), sv_weight(NULL),
00045 subkernel_weights_buffer(NULL), append_subkernel_weights(asw),
00046 num_lhs(0), num_rhs(0), initialized(false)
00047 {
00048 properties |= KP_LINADD | KP_KERNCOMBINATION | KP_BATCHEVALUATION;
00049 kernel_list=new CList<CKernel*>(true);
00050 SG_INFO("Combined kernel created (%p)\n", this) ;
00051 if (append_subkernel_weights)
00052 SG_INFO( "(subkernel weights are appended)\n") ;
00053 }
00054
00055 CCombinedKernel::~CCombinedKernel()
00056 {
00057 delete[] subkernel_weights_buffer;
00058 subkernel_weights_buffer=NULL;
00059
00060 cleanup();
00061 SG_UNREF(kernel_list);
00062
00063 SG_INFO("Combined kernel deleted (%p).\n", this);
00064 }
00065
00066 bool CCombinedKernel::init(CFeatures* l, CFeatures* r)
00067 {
00068 CKernel::init(l,r);
00069 ASSERT(l->get_feature_class()==C_COMBINED);
00070 ASSERT(r->get_feature_class()==C_COMBINED);
00071 ASSERT(l->get_feature_type()==F_UNKNOWN);
00072 ASSERT(r->get_feature_type()==F_UNKNOWN);
00073
00074 ASSERT(!num_lhs || num_lhs==l->get_num_vectors());
00075 ASSERT(!num_rhs || num_rhs==l->get_num_vectors());
00076
00077 num_lhs=l->get_num_vectors();
00078 num_rhs=r->get_num_vectors();
00079
00080 CFeatures* lf=NULL;
00081 CFeatures* rf=NULL;
00082 CKernel* k=NULL;
00083
00084 bool result=true;
00085
00086 CListElement<CFeatures*>* lfc = NULL;
00087 CListElement<CFeatures*>* rfc = NULL;
00088 lf=((CCombinedFeatures*) l)->get_first_feature_obj(lfc);
00089 rf=((CCombinedFeatures*) r)->get_first_feature_obj(rfc);
00090 CListElement<CKernel*>* current = NULL;
00091 k=get_first_kernel(current);
00092
00093 while ( result && k )
00094 {
00095
00096 if (k->get_kernel_type() != K_CUSTOM)
00097 {
00098 if (!lf || !rf)
00099 {
00100 SG_UNREF(lf);
00101 SG_UNREF(rf);
00102 SG_UNREF(k);
00103 SG_ERROR( "CombinedKernel: Number of features/kernels does not match - bailing out\n");
00104 }
00105
00106 SG_DEBUG( "Initializing 0x%p - \"%s\"\n", this, k->get_name());
00107 result=k->init(lf,rf);
00108 SG_UNREF(lf);
00109 SG_UNREF(rf);
00110
00111 lf=((CCombinedFeatures*) l)->get_next_feature_obj(lfc) ;
00112 rf=((CCombinedFeatures*) r)->get_next_feature_obj(rfc) ;
00113 }
00114 else
00115 {
00116 SG_DEBUG( "Initializing 0x%p - \"%s\" (skipping init, this is a CUSTOM kernel)\n", this, k->get_name());
00117 if (!k->has_features())
00118 SG_ERROR("No kernel matrix was assigned to this Custom kernel\n");
00119 if (!k->get_num_vec_lhs() != num_lhs)
00120 SG_ERROR("Number of lhs-feature vectors (%d) not match with number of rows (%d) of custom kernel\n", num_lhs, k->get_num_vec_lhs());
00121 if (!k->get_num_vec_rhs() != num_rhs)
00122 SG_ERROR("Number of rhs-feature vectors (%d) not match with number of cols (%d) of custom kernel\n", num_rhs, k->get_num_vec_rhs());
00123 }
00124
00125 SG_UNREF(k);
00126 k=get_next_kernel(current) ;
00127 }
00128
00129 if (!result)
00130 {
00131 SG_INFO( "CombinedKernel: Initialising the following kernel failed\n");
00132 if (k)
00133 k->list_kernel();
00134 else
00135 SG_INFO( "<NULL>\n");
00136 return false;
00137 }
00138
00139 if ((lf!=NULL) || (rf!=NULL) || (k!=NULL))
00140 {
00141 SG_UNREF(lf);
00142 SG_UNREF(rf);
00143 SG_UNREF(k);
00144 SG_ERROR( "CombinedKernel: Number of features/kernels does not match - bailing out\n");
00145 }
00146
00147 init_normalizer();
00148 initialized=true;
00149 return true;
00150 }
00151
00152 void CCombinedKernel::remove_lhs()
00153 {
00154 delete_optimization();
00155
00156 CListElement<CKernel*> * current = NULL ;
00157 CKernel* k=get_first_kernel(current);
00158
00159 while (k)
00160 {
00161 if (k->get_kernel_type() != K_CUSTOM)
00162 k->remove_lhs();
00163
00164 SG_UNREF(k);
00165 k=get_next_kernel(current);
00166 }
00167 CKernel::remove_lhs();
00168
00169 num_lhs=0;
00170 }
00171
00172 void CCombinedKernel::remove_rhs()
00173 {
00174 CListElement<CKernel*> * current = NULL ;
00175 CKernel* k=get_first_kernel(current);
00176
00177 while (k)
00178 {
00179 if (k->get_kernel_type() != K_CUSTOM)
00180 k->remove_rhs();
00181 SG_UNREF(k);
00182 k=get_next_kernel(current);
00183 }
00184 CKernel::remove_rhs();
00185
00186 num_rhs=0;
00187 }
00188
00189 void CCombinedKernel::remove_lhs_and_rhs()
00190 {
00191 delete_optimization();
00192
00193 CListElement<CKernel*> * current = NULL ;
00194 CKernel* k=get_first_kernel(current);
00195
00196 while (k)
00197 {
00198 if (k->get_kernel_type() != K_CUSTOM)
00199 k->remove_lhs_and_rhs();
00200 SG_UNREF(k);
00201 k=get_next_kernel(current);
00202 }
00203
00204 CKernel::remove_lhs_and_rhs();
00205
00206 num_lhs=0;
00207 num_rhs=0;
00208 }
00209
00210 void CCombinedKernel::cleanup()
00211 {
00212 CListElement<CKernel*> * current = NULL ;
00213 CKernel* k=get_first_kernel(current);
00214
00215 while (k)
00216 {
00217 k->cleanup();
00218 SG_UNREF(k);
00219 k=get_next_kernel(current);
00220 }
00221
00222 delete_optimization();
00223
00224 CKernel::cleanup();
00225
00226 num_lhs=0;
00227 num_rhs=0;
00228 }
00229
00230 void CCombinedKernel::list_kernels()
00231 {
00232 CKernel* k;
00233
00234 SG_INFO( "BEGIN COMBINED KERNEL LIST - ");
00235 this->list_kernel();
00236
00237 CListElement<CKernel*> * current = NULL ;
00238 k=get_first_kernel(current);
00239 while (k)
00240 {
00241 k->list_kernel();
00242 SG_UNREF(k);
00243 k=get_next_kernel(current);
00244 }
00245 SG_INFO( "END COMBINED KERNEL LIST - ");
00246 }
00247
00248 float64_t CCombinedKernel::compute(int32_t x, int32_t y)
00249 {
00250 float64_t result=0;
00251 CListElement<CKernel*> * current = NULL ;
00252 CKernel* k=get_first_kernel(current);
00253 while (k)
00254 {
00255 if (k->get_combined_kernel_weight()!=0)
00256 result += k->get_combined_kernel_weight() * k->kernel(x,y);
00257 SG_UNREF(k);
00258 k=get_next_kernel(current);
00259 }
00260
00261 return result;
00262 }
00263
00264 bool CCombinedKernel::init_optimization(
00265 int32_t count, int32_t *IDX, float64_t *weights)
00266 {
00267 SG_DEBUG( "initializing CCombinedKernel optimization\n");
00268
00269 delete_optimization();
00270
00271 CListElement<CKernel*> *current=NULL;
00272 CKernel *k=get_first_kernel(current);
00273 bool have_non_optimizable=false;
00274
00275 while(k)
00276 {
00277 bool ret=true;
00278
00279 if (k && k->has_property(KP_LINADD))
00280 ret=k->init_optimization(count, IDX, weights);
00281 else
00282 {
00283 SG_WARNING("non-optimizable kernel 0x%X in kernel-list\n", k);
00284 have_non_optimizable=true;
00285 }
00286
00287 if (!ret)
00288 {
00289 have_non_optimizable=true;
00290 SG_WARNING("init_optimization of kernel 0x%X failed\n", k);
00291 }
00292
00293 SG_UNREF(k);
00294 k=get_next_kernel(current);
00295 }
00296
00297 if (have_non_optimizable)
00298 {
00299 SG_WARNING( "some kernels in the kernel-list are not optimized\n");
00300
00301 sv_idx=new int32_t[count];
00302 sv_weight=new float64_t[count];
00303 sv_count=count;
00304 for (int32_t i=0; i<count; i++)
00305 {
00306 sv_idx[i]=IDX[i];
00307 sv_weight[i]=weights[i];
00308 }
00309 }
00310 set_is_initialized(true);
00311
00312 return true;
00313 }
00314
00315 bool CCombinedKernel::delete_optimization()
00316 {
00317 CListElement<CKernel*> * current = NULL ;
00318 CKernel* k = get_first_kernel(current);
00319
00320 while(k)
00321 {
00322 if (k->has_property(KP_LINADD))
00323 k->delete_optimization();
00324
00325 SG_UNREF(k);
00326 k = get_next_kernel(current);
00327 }
00328
00329 delete[] sv_idx;
00330 sv_idx = NULL;
00331
00332 delete[] sv_weight;
00333 sv_weight = NULL;
00334
00335 sv_count = 0;
00336 set_is_initialized(false);
00337
00338 return true;
00339 }
00340
00341 void CCombinedKernel::compute_batch(
00342 int32_t num_vec, int32_t* vec_idx, float64_t* result, int32_t num_suppvec,
00343 int32_t* IDX, float64_t* weights, float64_t factor)
00344 {
00345 ASSERT(num_vec<=get_num_vec_rhs())
00346 ASSERT(num_vec>0);
00347 ASSERT(vec_idx);
00348 ASSERT(result);
00349
00350
00351
00352 delete_optimization();
00353
00354 CListElement<CKernel*> * current = NULL ;
00355 CKernel * k = get_first_kernel(current) ;
00356
00357 while(k)
00358 {
00359 if (k && k->has_property(KP_BATCHEVALUATION))
00360 {
00361 if (k->get_combined_kernel_weight()!=0)
00362 k->compute_batch(num_vec, vec_idx, result, num_suppvec, IDX, weights, k->get_combined_kernel_weight());
00363 }
00364 else
00365 emulate_compute_batch(k, num_vec, vec_idx, result, num_suppvec, IDX, weights);
00366
00367 SG_UNREF(k);
00368 k = get_next_kernel(current);
00369 }
00370
00371
00372 delete_optimization();
00373 }
00374
00375 void* CCombinedKernel::compute_optimized_kernel_helper(void* p)
00376 {
00377 S_THREAD_PARAM* params= (S_THREAD_PARAM*) p;
00378 int32_t* vec_idx=params->vec_idx;
00379 CKernel* k=params->kernel;
00380 float64_t* result=params->result;
00381
00382 for (int32_t i=params->start; i<params->end; i++)
00383 result[i] += k->get_combined_kernel_weight()*k->compute_optimized(vec_idx[i]);
00384
00385 return NULL;
00386 }
00387
00388 void* CCombinedKernel::compute_kernel_helper(void* p)
00389 {
00390 S_THREAD_PARAM* params= (S_THREAD_PARAM*) p;
00391 int32_t* vec_idx=params->vec_idx;
00392 CKernel* k=params->kernel;
00393 float64_t* result=params->result;
00394 float64_t* weights=params->weights;
00395 int32_t* IDX=params->IDX;
00396 int32_t num_suppvec=params->num_suppvec;
00397
00398 for (int32_t i=params->start; i<params->end; i++)
00399 {
00400 float64_t sub_result=0;
00401 for (int32_t j=0; j<num_suppvec; j++)
00402 sub_result += weights[j] * k->kernel(IDX[j], vec_idx[i]);
00403
00404 result[i] += k->get_combined_kernel_weight()*sub_result;
00405 }
00406
00407 return NULL;
00408 }
00409
00410 void CCombinedKernel::emulate_compute_batch(
00411 CKernel* k, int32_t num_vec, int32_t* vec_idx, float64_t* result,
00412 int32_t num_suppvec, int32_t* IDX, float64_t* weights)
00413 {
00414 ASSERT(k);
00415 ASSERT(result);
00416
00417 if (k->has_property(KP_LINADD))
00418 {
00419 if (k->get_combined_kernel_weight()!=0)
00420 {
00421 k->init_optimization(num_suppvec, IDX, weights);
00422
00423 int32_t num_threads=parallel->get_num_threads();
00424 ASSERT(num_threads>0);
00425
00426 if (num_threads < 2)
00427 {
00428 S_THREAD_PARAM params;
00429 params.kernel=k;
00430 params.result=result;
00431 params.start=0;
00432 params.end=num_vec;
00433 params.vec_idx = vec_idx;
00434 compute_optimized_kernel_helper((void*) ¶ms);
00435 }
00436 #ifndef WIN32
00437 else
00438 {
00439 pthread_t* threads = new pthread_t[num_threads-1];
00440 S_THREAD_PARAM* params = new S_THREAD_PARAM[num_threads];
00441 int32_t step= num_vec/num_threads;
00442
00443 int32_t t;
00444
00445 for (t=0; t<num_threads-1; t++)
00446 {
00447 params[t].kernel = k;
00448 params[t].result = result;
00449 params[t].start = t*step;
00450 params[t].end = (t+1)*step;
00451 params[t].vec_idx = vec_idx;
00452 pthread_create(&threads[t], NULL, CCombinedKernel::compute_optimized_kernel_helper, (void*)¶ms[t]);
00453 }
00454
00455 params[t].kernel = k;
00456 params[t].result = result;
00457 params[t].start = t*step;
00458 params[t].end = num_vec;
00459 params[t].vec_idx = vec_idx;
00460 compute_optimized_kernel_helper((void*) ¶ms[t]);
00461
00462 for (t=0; t<num_threads-1; t++)
00463 pthread_join(threads[t], NULL);
00464
00465 delete[] params;
00466 delete[] threads;
00467 }
00468 #endif
00469
00470 k->delete_optimization();
00471 }
00472 }
00473 else
00474 {
00475 ASSERT(IDX!=NULL || num_suppvec==0);
00476 ASSERT(weights!=NULL || num_suppvec==0);
00477
00478 if (k->get_combined_kernel_weight()!=0)
00479 {
00480 int32_t num_threads=parallel->get_num_threads();
00481 ASSERT(num_threads>0);
00482
00483 if (num_threads < 2)
00484 {
00485 S_THREAD_PARAM params;
00486 params.kernel=k;
00487 params.result=result;
00488 params.start=0;
00489 params.end=num_vec;
00490 params.vec_idx = vec_idx;
00491 params.IDX = IDX;
00492 params.weights = weights;
00493 params.num_suppvec = num_suppvec;
00494 compute_kernel_helper((void*) ¶ms);
00495 }
00496 #ifndef WIN32
00497 else
00498 {
00499 pthread_t* threads = new pthread_t[num_threads-1];
00500 S_THREAD_PARAM* params = new S_THREAD_PARAM[num_threads];
00501 int32_t step= num_vec/num_threads;
00502
00503 int32_t t;
00504
00505 for (t=0; t<num_threads-1; t++)
00506 {
00507 params[t].kernel = k;
00508 params[t].result = result;
00509 params[t].start = t*step;
00510 params[t].end = (t+1)*step;
00511 params[t].vec_idx = vec_idx;
00512 params[t].IDX = IDX;
00513 params[t].weights = weights;
00514 params[t].num_suppvec = num_suppvec;
00515 pthread_create(&threads[t], NULL, CCombinedKernel::compute_kernel_helper, (void*)¶ms[t]);
00516 }
00517
00518 params[t].kernel = k;
00519 params[t].result = result;
00520 params[t].start = t*step;
00521 params[t].end = num_vec;
00522 params[t].vec_idx = vec_idx;
00523 params[t].IDX = IDX;
00524 params[t].weights = weights;
00525 params[t].num_suppvec = num_suppvec;
00526 compute_kernel_helper(¶ms[t]);
00527
00528 for (t=0; t<num_threads-1; t++)
00529 pthread_join(threads[t], NULL);
00530
00531 delete[] params;
00532 delete[] threads;
00533 }
00534 #endif
00535 }
00536 }
00537 }
00538
00539 float64_t CCombinedKernel::compute_optimized(int32_t idx)
00540 {
00541 if (!get_is_initialized())
00542 {
00543 SG_ERROR("CCombinedKernel optimization not initialized\n");
00544 return 0;
00545 }
00546
00547 float64_t result=0;
00548
00549 CListElement<CKernel*> *current=NULL;
00550 CKernel *k=get_first_kernel(current);
00551 while (k)
00552 {
00553 if (k->has_property(KP_LINADD) &&
00554 k->get_is_initialized())
00555 {
00556 if (k->get_combined_kernel_weight()!=0)
00557 {
00558 result +=
00559 k->get_combined_kernel_weight()*k->compute_optimized(idx);
00560 }
00561 }
00562 else
00563 {
00564 ASSERT(sv_idx!=NULL || sv_count==0);
00565 ASSERT(sv_weight!=NULL || sv_count==0);
00566
00567 if (k->get_combined_kernel_weight()!=0)
00568 {
00569 float64_t sub_result=0;
00570 for (int32_t j=0; j<sv_count; j++)
00571 sub_result += sv_weight[j] * k->kernel(sv_idx[j], idx);
00572
00573 result += k->get_combined_kernel_weight()*sub_result;
00574 }
00575 }
00576
00577 SG_UNREF(k);
00578 k=get_next_kernel(current);
00579 }
00580
00581 return result;
00582 }
00583
00584 void CCombinedKernel::add_to_normal(int32_t idx, float64_t weight)
00585 {
00586 CListElement<CKernel*> * current = NULL ;
00587 CKernel* k = get_first_kernel(current);
00588
00589 while(k)
00590 {
00591 k->add_to_normal(idx, weight);
00592 SG_UNREF(k);
00593 k = get_next_kernel(current);
00594 }
00595 set_is_initialized(true) ;
00596 }
00597
00598 void CCombinedKernel::clear_normal()
00599 {
00600 CListElement<CKernel*> * current = NULL ;
00601 CKernel* k = get_first_kernel(current);
00602
00603 while(k)
00604 {
00605 k->clear_normal() ;
00606 SG_UNREF(k);
00607 k = get_next_kernel(current);
00608 }
00609 set_is_initialized(true) ;
00610 }
00611
00612 void CCombinedKernel::compute_by_subkernel(
00613 int32_t idx, float64_t * subkernel_contrib)
00614 {
00615 if (append_subkernel_weights)
00616 {
00617 int32_t i=0 ;
00618 CListElement<CKernel*> * current = NULL ;
00619 CKernel* k = get_first_kernel(current);
00620 while(k)
00621 {
00622 int32_t num = -1 ;
00623 k->get_subkernel_weights(num);
00624 if (num>1)
00625 k->compute_by_subkernel(idx, &subkernel_contrib[i]) ;
00626 else
00627 subkernel_contrib[i] += k->get_combined_kernel_weight() * k->compute_optimized(idx) ;
00628
00629 SG_UNREF(k);
00630 k = get_next_kernel(current);
00631 i += num ;
00632 }
00633 }
00634 else
00635 {
00636 int32_t i=0 ;
00637 CListElement<CKernel*> * current = NULL ;
00638 CKernel* k = get_first_kernel(current);
00639 while(k)
00640 {
00641 if (k->get_combined_kernel_weight()!=0)
00642 subkernel_contrib[i] += k->get_combined_kernel_weight() * k->compute_optimized(idx) ;
00643
00644 SG_UNREF(k);
00645 k = get_next_kernel(current);
00646 i++ ;
00647 }
00648 }
00649 }
00650
00651 const float64_t* CCombinedKernel::get_subkernel_weights(int32_t& num_weights)
00652 {
00653 num_weights = get_num_subkernels() ;
00654 delete[] subkernel_weights_buffer ;
00655 subkernel_weights_buffer = new float64_t[num_weights] ;
00656
00657 if (append_subkernel_weights)
00658 {
00659 int32_t i=0 ;
00660 CListElement<CKernel*> * current = NULL ;
00661 CKernel* k = get_first_kernel(current);
00662 while(k)
00663 {
00664 int32_t num = -1 ;
00665 const float64_t *w = k->get_subkernel_weights(num);
00666 ASSERT(num==k->get_num_subkernels());
00667 for (int32_t j=0; j<num; j++)
00668 subkernel_weights_buffer[i+j]=w[j] ;
00669
00670 SG_UNREF(k);
00671 k = get_next_kernel(current);
00672 i += num ;
00673 }
00674 }
00675 else
00676 {
00677 int32_t i=0 ;
00678 CListElement<CKernel*> * current = NULL ;
00679 CKernel* k = get_first_kernel(current);
00680 while(k)
00681 {
00682 subkernel_weights_buffer[i] = k->get_combined_kernel_weight();
00683
00684 SG_UNREF(k);
00685 k = get_next_kernel(current);
00686 i++ ;
00687 }
00688 }
00689
00690 return subkernel_weights_buffer ;
00691 }
00692
00693 void CCombinedKernel::get_subkernel_weights(float64_t** weights, int32_t* num_weights)
00694 {
00695 int32_t num=0;
00696 const float64_t* w=get_subkernel_weights(num);
00697
00698 ASSERT(num>0);
00699 *num_weights=num;
00700 *weights = (float64_t*) malloc(num*sizeof(float64_t));
00701 memcpy(*weights, w, num*sizeof(float64_t));
00702 }
00703
00704 void CCombinedKernel::set_subkernel_weights(
00705 float64_t* weights, int32_t num_weights)
00706 {
00707 if (append_subkernel_weights)
00708 {
00709 int32_t i=0 ;
00710 CListElement<CKernel*> * current = NULL ;
00711 CKernel* k = get_first_kernel(current);
00712 while(k)
00713 {
00714 int32_t num = k->get_num_subkernels() ;
00715 k->set_subkernel_weights(&weights[i],num);
00716
00717 SG_UNREF(k);
00718 k = get_next_kernel(current);
00719 i += num ;
00720 }
00721 }
00722 else
00723 {
00724 int32_t i=0 ;
00725 CListElement<CKernel*> * current = NULL ;
00726 CKernel* k = get_first_kernel(current);
00727 while(k)
00728 {
00729 k->set_combined_kernel_weight(weights[i]);
00730
00731 SG_UNREF(k);
00732 k = get_next_kernel(current);
00733 i++ ;
00734 }
00735 }
00736 }
00737
00738 void CCombinedKernel::set_optimization_type(EOptimizationType t)
00739 {
00740 CKernel* k = get_first_kernel();
00741
00742 while(k)
00743 {
00744 k->set_optimization_type(t);
00745
00746 SG_UNREF(k);
00747 k = get_next_kernel();
00748 }
00749
00750 CKernel::set_optimization_type(t);
00751 }
00752
00753 bool CCombinedKernel::precompute_subkernels()
00754 {
00755 CKernel* k = get_first_kernel();
00756
00757 if (!k)
00758 return false;
00759
00760 CList<CKernel*>* new_kernel_list = new CList<CKernel*>(true);
00761
00762 while(k)
00763 {
00764 new_kernel_list->append_element(new CCustomKernel(k));
00765
00766 SG_UNREF(k);
00767 k = get_next_kernel();
00768 }
00769
00770 SG_UNREF(kernel_list);
00771 kernel_list=new_kernel_list;
00772
00773 return true;
00774 }