[16] | 1 | /******************************************************************** |
---|
| 2 | * * |
---|
| 3 | * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * |
---|
| 4 | * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * |
---|
| 5 | * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * |
---|
| 6 | * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * |
---|
| 7 | * * |
---|
| 8 | * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 * |
---|
| 9 | * by the Xiph.Org Foundation http://www.xiph.org/ * |
---|
| 10 | * * |
---|
| 11 | ******************************************************************** |
---|
| 12 | |
---|
| 13 | function: train a VQ codebook |
---|
| 14 | last mod: $Id: vqgen.c 13293 2007-07-24 00:09:47Z xiphmont $ |
---|
| 15 | |
---|
| 16 | ********************************************************************/ |
---|
| 17 | |
---|
| 18 | /* This code is *not* part of libvorbis. It is used to generate |
---|
| 19 | trained codebooks offline and then spit the results into a |
---|
| 20 | pregenerated codebook that is compiled into libvorbis. It is an |
---|
| 21 | expensive (but good) algorithm. Run it on big iron. */ |
---|
| 22 | |
---|
| 23 | /* There are so many optimizations to explore in *both* stages that |
---|
| 24 | considering the undertaking is almost withering. For now, we brute |
---|
| 25 | force it all */ |
---|
| 26 | |
---|
| 27 | #include <stdlib.h> |
---|
| 28 | #include <stdio.h> |
---|
| 29 | #include <math.h> |
---|
| 30 | #include <string.h> |
---|
| 31 | |
---|
| 32 | #include "vqgen.h" |
---|
| 33 | #include "bookutil.h" |
---|
| 34 | |
---|
| 35 | /* Codebook generation happens in two steps: |
---|
| 36 | |
---|
| 37 | 1) Train the codebook with data collected from the encoder: We use |
---|
| 38 | one of a few error metrics (which represent the distance between a |
---|
| 39 | given data point and a candidate point in the training set) to |
---|
| 40 | divide the training set up into cells representing roughly equal |
---|
| 41 | probability of occurring. |
---|
| 42 | |
---|
| 43 | 2) Generate the codebook and auxiliary data from the trained data set |
---|
| 44 | */ |
---|
| 45 | |
---|
| 46 | /* Codebook training **************************************************** |
---|
| 47 | * |
---|
| 48 | * The basic idea here is that a VQ codebook is like an m-dimensional |
---|
| 49 | * foam with n bubbles. The bubbles compete for space/volume and are |
---|
| 50 | * 'pressurized' [biased] according to some metric. The basic alg |
---|
| 51 | * iterates through allowing the bubbles to compete for space until |
---|
| 52 | * they converge (if the damping is dome properly) on a steady-state |
---|
| 53 | * solution. Individual input points, collected from libvorbis, are |
---|
| 54 | * used to train the algorithm monte-carlo style. */ |
---|
| 55 | |
---|
| 56 | /* internal helpers *****************************************************/ |
---|
| 57 | #define vN(data,i) (data+v->elements*i) |
---|
| 58 | |
---|
| 59 | /* default metric; squared 'distance' from desired value. */ |
---|
| 60 | float _dist(vqgen *v,float *a, float *b){ |
---|
| 61 | int i; |
---|
| 62 | int el=v->elements; |
---|
| 63 | float acc=0.f; |
---|
| 64 | for(i=0;i<el;i++){ |
---|
| 65 | float val=(a[i]-b[i]); |
---|
| 66 | acc+=val*val; |
---|
| 67 | } |
---|
| 68 | return sqrt(acc); |
---|
| 69 | } |
---|
| 70 | |
---|
| 71 | float *_weight_null(vqgen *v,float *a){ |
---|
| 72 | return a; |
---|
| 73 | } |
---|
| 74 | |
---|
| 75 | /* *must* be beefed up. */ |
---|
| 76 | void _vqgen_seed(vqgen *v){ |
---|
| 77 | long i; |
---|
| 78 | for(i=0;i<v->entries;i++) |
---|
| 79 | memcpy(_now(v,i),_point(v,i),sizeof(float)*v->elements); |
---|
| 80 | v->seeded=1; |
---|
| 81 | } |
---|
| 82 | |
---|
| 83 | int directdsort(const void *a, const void *b){ |
---|
| 84 | float av=*((float *)a); |
---|
| 85 | float bv=*((float *)b); |
---|
| 86 | return (av<bv)-(av>bv); |
---|
| 87 | } |
---|
| 88 | |
---|
| 89 | void vqgen_cellmetric(vqgen *v){ |
---|
| 90 | int j,k; |
---|
| 91 | float min=-1.f,max=-1.f,mean=0.f,acc=0.f; |
---|
| 92 | long dup=0,unused=0; |
---|
| 93 | #ifdef NOISY |
---|
| 94 | int i; |
---|
| 95 | char buff[80]; |
---|
| 96 | float spacings[v->entries]; |
---|
| 97 | int count=0; |
---|
| 98 | FILE *cells; |
---|
| 99 | sprintf(buff,"cellspace%d.m",v->it); |
---|
| 100 | cells=fopen(buff,"w"); |
---|
| 101 | #endif |
---|
| 102 | |
---|
| 103 | /* minimum, maximum, cell spacing */ |
---|
| 104 | for(j=0;j<v->entries;j++){ |
---|
| 105 | float localmin=-1.; |
---|
| 106 | |
---|
| 107 | for(k=0;k<v->entries;k++){ |
---|
| 108 | if(j!=k){ |
---|
| 109 | float this=_dist(v,_now(v,j),_now(v,k)); |
---|
| 110 | if(this>0){ |
---|
| 111 | if(v->assigned[k] && (localmin==-1 || this<localmin)) |
---|
| 112 | localmin=this; |
---|
| 113 | }else{ |
---|
| 114 | if(k<j){ |
---|
| 115 | dup++; |
---|
| 116 | break; |
---|
| 117 | } |
---|
| 118 | } |
---|
| 119 | } |
---|
| 120 | } |
---|
| 121 | if(k<v->entries)continue; |
---|
| 122 | |
---|
| 123 | if(v->assigned[j]==0){ |
---|
| 124 | unused++; |
---|
| 125 | continue; |
---|
| 126 | } |
---|
| 127 | |
---|
| 128 | localmin=v->max[j]+localmin/2; /* this gives us rough diameter */ |
---|
| 129 | if(min==-1 || localmin<min)min=localmin; |
---|
| 130 | if(max==-1 || localmin>max)max=localmin; |
---|
| 131 | mean+=localmin; |
---|
| 132 | acc++; |
---|
| 133 | #ifdef NOISY |
---|
| 134 | spacings[count++]=localmin; |
---|
| 135 | #endif |
---|
| 136 | } |
---|
| 137 | |
---|
| 138 | fprintf(stderr,"cell diameter: %.03g::%.03g::%.03g (%ld unused/%ld dup)\n", |
---|
| 139 | min,mean/acc,max,unused,dup); |
---|
| 140 | |
---|
| 141 | #ifdef NOISY |
---|
| 142 | qsort(spacings,count,sizeof(float),directdsort); |
---|
| 143 | for(i=0;i<count;i++) |
---|
| 144 | fprintf(cells,"%g\n",spacings[i]); |
---|
| 145 | fclose(cells); |
---|
| 146 | #endif |
---|
| 147 | |
---|
| 148 | } |
---|
| 149 | |
---|
| 150 | /* External calls *******************************************************/ |
---|
| 151 | |
---|
| 152 | /* We have two forms of quantization; in the first, each vector |
---|
| 153 | element in the codebook entry is orthogonal. Residues would use this |
---|
| 154 | quantization for example. |
---|
| 155 | |
---|
| 156 | In the second, we have a sequence of monotonically increasing |
---|
| 157 | values that we wish to quantize as deltas (to save space). We |
---|
| 158 | still need to quantize so that absolute values are accurate. For |
---|
| 159 | example, LSP quantizes all absolute values, but the book encodes |
---|
| 160 | distance between values because each successive value is larger |
---|
| 161 | than the preceeding value. Thus the desired quantibits apply to |
---|
| 162 | the encoded (delta) values, not abs positions. This requires minor |
---|
| 163 | additional encode-side trickery. */ |
---|
| 164 | |
---|
| 165 | void vqgen_quantize(vqgen *v,quant_meta *q){ |
---|
| 166 | |
---|
| 167 | float maxdel; |
---|
| 168 | float mindel; |
---|
| 169 | |
---|
| 170 | float delta; |
---|
| 171 | float maxquant=((1<<q->quant)-1); |
---|
| 172 | |
---|
| 173 | int j,k; |
---|
| 174 | |
---|
| 175 | mindel=maxdel=_now(v,0)[0]; |
---|
| 176 | |
---|
| 177 | for(j=0;j<v->entries;j++){ |
---|
| 178 | float last=0.f; |
---|
| 179 | for(k=0;k<v->elements;k++){ |
---|
| 180 | if(mindel>_now(v,j)[k]-last)mindel=_now(v,j)[k]-last; |
---|
| 181 | if(maxdel<_now(v,j)[k]-last)maxdel=_now(v,j)[k]-last; |
---|
| 182 | if(q->sequencep)last=_now(v,j)[k]; |
---|
| 183 | } |
---|
| 184 | } |
---|
| 185 | |
---|
| 186 | |
---|
| 187 | /* first find the basic delta amount from the maximum span to be |
---|
| 188 | encoded. Loosen the delta slightly to allow for additional error |
---|
| 189 | during sequence quantization */ |
---|
| 190 | |
---|
| 191 | delta=(maxdel-mindel)/((1<<q->quant)-1.5f); |
---|
| 192 | |
---|
| 193 | q->min=_float32_pack(mindel); |
---|
| 194 | q->delta=_float32_pack(delta); |
---|
| 195 | |
---|
| 196 | mindel=_float32_unpack(q->min); |
---|
| 197 | delta=_float32_unpack(q->delta); |
---|
| 198 | |
---|
| 199 | for(j=0;j<v->entries;j++){ |
---|
| 200 | float last=0; |
---|
| 201 | for(k=0;k<v->elements;k++){ |
---|
| 202 | float val=_now(v,j)[k]; |
---|
| 203 | float now=rint((val-last-mindel)/delta); |
---|
| 204 | |
---|
| 205 | _now(v,j)[k]=now; |
---|
| 206 | if(now<0){ |
---|
| 207 | /* be paranoid; this should be impossible */ |
---|
| 208 | fprintf(stderr,"fault; quantized value<0\n"); |
---|
| 209 | exit(1); |
---|
| 210 | } |
---|
| 211 | |
---|
| 212 | if(now>maxquant){ |
---|
| 213 | /* be paranoid; this should be impossible */ |
---|
| 214 | fprintf(stderr,"fault; quantized value>max\n"); |
---|
| 215 | exit(1); |
---|
| 216 | } |
---|
| 217 | if(q->sequencep)last=(now*delta)+mindel+last; |
---|
| 218 | } |
---|
| 219 | } |
---|
| 220 | } |
---|
| 221 | |
---|
| 222 | /* much easier :-). Unlike in the codebook, we don't un-log log |
---|
| 223 | scales; we just make sure they're properly offset. */ |
---|
| 224 | void vqgen_unquantize(vqgen *v,quant_meta *q){ |
---|
| 225 | long j,k; |
---|
| 226 | float mindel=_float32_unpack(q->min); |
---|
| 227 | float delta=_float32_unpack(q->delta); |
---|
| 228 | |
---|
| 229 | for(j=0;j<v->entries;j++){ |
---|
| 230 | float last=0.f; |
---|
| 231 | for(k=0;k<v->elements;k++){ |
---|
| 232 | float now=_now(v,j)[k]; |
---|
| 233 | now=fabs(now)*delta+last+mindel; |
---|
| 234 | if(q->sequencep)last=now; |
---|
| 235 | _now(v,j)[k]=now; |
---|
| 236 | } |
---|
| 237 | } |
---|
| 238 | } |
---|
| 239 | |
---|
| 240 | void vqgen_init(vqgen *v,int elements,int aux,int entries,float mindist, |
---|
| 241 | float (*metric)(vqgen *,float *, float *), |
---|
| 242 | float *(*weight)(vqgen *,float *),int centroid){ |
---|
| 243 | memset(v,0,sizeof(vqgen)); |
---|
| 244 | |
---|
| 245 | v->centroid=centroid; |
---|
| 246 | v->elements=elements; |
---|
| 247 | v->aux=aux; |
---|
| 248 | v->mindist=mindist; |
---|
| 249 | v->allocated=32768; |
---|
| 250 | v->pointlist=_ogg_malloc(v->allocated*(v->elements+v->aux)*sizeof(float)); |
---|
| 251 | |
---|
| 252 | v->entries=entries; |
---|
| 253 | v->entrylist=_ogg_malloc(v->entries*v->elements*sizeof(float)); |
---|
| 254 | v->assigned=_ogg_malloc(v->entries*sizeof(long)); |
---|
| 255 | v->bias=_ogg_calloc(v->entries,sizeof(float)); |
---|
| 256 | v->max=_ogg_calloc(v->entries,sizeof(float)); |
---|
| 257 | if(metric) |
---|
| 258 | v->metric_func=metric; |
---|
| 259 | else |
---|
| 260 | v->metric_func=_dist; |
---|
| 261 | if(weight) |
---|
| 262 | v->weight_func=weight; |
---|
| 263 | else |
---|
| 264 | v->weight_func=_weight_null; |
---|
| 265 | |
---|
| 266 | v->asciipoints=tmpfile(); |
---|
| 267 | |
---|
| 268 | } |
---|
| 269 | |
---|
| 270 | void vqgen_addpoint(vqgen *v, float *p,float *a){ |
---|
| 271 | int k; |
---|
| 272 | for(k=0;k<v->elements;k++) |
---|
| 273 | fprintf(v->asciipoints,"%.12g\n",p[k]); |
---|
| 274 | for(k=0;k<v->aux;k++) |
---|
| 275 | fprintf(v->asciipoints,"%.12g\n",a[k]); |
---|
| 276 | |
---|
| 277 | if(v->points>=v->allocated){ |
---|
| 278 | v->allocated*=2; |
---|
| 279 | v->pointlist=_ogg_realloc(v->pointlist,v->allocated*(v->elements+v->aux)* |
---|
| 280 | sizeof(float)); |
---|
| 281 | } |
---|
| 282 | |
---|
| 283 | memcpy(_point(v,v->points),p,sizeof(float)*v->elements); |
---|
| 284 | if(v->aux)memcpy(_point(v,v->points)+v->elements,a,sizeof(float)*v->aux); |
---|
| 285 | |
---|
| 286 | /* quantize to the density mesh if it's selected */ |
---|
| 287 | if(v->mindist>0.f){ |
---|
| 288 | /* quantize to the mesh */ |
---|
| 289 | for(k=0;k<v->elements+v->aux;k++) |
---|
| 290 | _point(v,v->points)[k]= |
---|
| 291 | rint(_point(v,v->points)[k]/v->mindist)*v->mindist; |
---|
| 292 | } |
---|
| 293 | v->points++; |
---|
| 294 | if(!(v->points&0xff))spinnit("loading... ",v->points); |
---|
| 295 | } |
---|
| 296 | |
---|
| 297 | /* yes, not threadsafe. These utils aren't */ |
---|
| 298 | static int sortit=0; |
---|
| 299 | static int sortsize=0; |
---|
| 300 | static int meshcomp(const void *a,const void *b){ |
---|
| 301 | if(((sortit++)&0xfff)==0)spinnit("sorting mesh...",sortit); |
---|
| 302 | return(memcmp(a,b,sortsize)); |
---|
| 303 | } |
---|
| 304 | |
---|
| 305 | void vqgen_sortmesh(vqgen *v){ |
---|
| 306 | sortit=0; |
---|
| 307 | if(v->mindist>0.f){ |
---|
| 308 | long i,march=1; |
---|
| 309 | |
---|
| 310 | /* sort to make uniqueness detection trivial */ |
---|
| 311 | sortsize=(v->elements+v->aux)*sizeof(float); |
---|
| 312 | qsort(v->pointlist,v->points,sortsize,meshcomp); |
---|
| 313 | |
---|
| 314 | /* now march through and eliminate dupes */ |
---|
| 315 | for(i=1;i<v->points;i++){ |
---|
| 316 | if(memcmp(_point(v,i),_point(v,i-1),sortsize)){ |
---|
| 317 | /* a new, unique entry. march it down */ |
---|
| 318 | if(i>march)memcpy(_point(v,march),_point(v,i),sortsize); |
---|
| 319 | march++; |
---|
| 320 | } |
---|
| 321 | spinnit("eliminating density... ",v->points-i); |
---|
| 322 | } |
---|
| 323 | |
---|
| 324 | /* we're done */ |
---|
| 325 | fprintf(stderr,"\r%ld training points remining out of %ld" |
---|
| 326 | " after density mesh (%ld%%)\n",march,v->points,march*100/v->points); |
---|
| 327 | v->points=march; |
---|
| 328 | |
---|
| 329 | } |
---|
| 330 | v->sorted=1; |
---|
| 331 | } |
---|
| 332 | |
---|
| 333 | float vqgen_iterate(vqgen *v,int biasp){ |
---|
| 334 | long i,j,k; |
---|
| 335 | |
---|
| 336 | float fdesired; |
---|
| 337 | long desired; |
---|
| 338 | long desired2; |
---|
| 339 | |
---|
| 340 | float asserror=0.f; |
---|
| 341 | float meterror=0.f; |
---|
| 342 | float *new; |
---|
| 343 | float *new2; |
---|
| 344 | long *nearcount; |
---|
| 345 | float *nearbias; |
---|
| 346 | #ifdef NOISY |
---|
| 347 | char buff[80]; |
---|
| 348 | FILE *assig; |
---|
| 349 | FILE *bias; |
---|
| 350 | FILE *cells; |
---|
| 351 | sprintf(buff,"cells%d.m",v->it); |
---|
| 352 | cells=fopen(buff,"w"); |
---|
| 353 | sprintf(buff,"assig%d.m",v->it); |
---|
| 354 | assig=fopen(buff,"w"); |
---|
| 355 | sprintf(buff,"bias%d.m",v->it); |
---|
| 356 | bias=fopen(buff,"w"); |
---|
| 357 | #endif |
---|
| 358 | |
---|
| 359 | |
---|
| 360 | if(v->entries<2){ |
---|
| 361 | fprintf(stderr,"generation requires at least two entries\n"); |
---|
| 362 | exit(1); |
---|
| 363 | } |
---|
| 364 | |
---|
| 365 | if(!v->sorted)vqgen_sortmesh(v); |
---|
| 366 | if(!v->seeded)_vqgen_seed(v); |
---|
| 367 | |
---|
| 368 | fdesired=(float)v->points/v->entries; |
---|
| 369 | desired=fdesired; |
---|
| 370 | desired2=desired*2; |
---|
| 371 | new=_ogg_malloc(sizeof(float)*v->entries*v->elements); |
---|
| 372 | new2=_ogg_malloc(sizeof(float)*v->entries*v->elements); |
---|
| 373 | nearcount=_ogg_malloc(v->entries*sizeof(long)); |
---|
| 374 | nearbias=_ogg_malloc(v->entries*desired2*sizeof(float)); |
---|
| 375 | |
---|
| 376 | /* fill in nearest points for entry biasing */ |
---|
| 377 | /*memset(v->bias,0,sizeof(float)*v->entries);*/ |
---|
| 378 | memset(nearcount,0,sizeof(long)*v->entries); |
---|
| 379 | memset(v->assigned,0,sizeof(long)*v->entries); |
---|
| 380 | if(biasp){ |
---|
| 381 | for(i=0;i<v->points;i++){ |
---|
| 382 | float *ppt=v->weight_func(v,_point(v,i)); |
---|
| 383 | float firstmetric=v->metric_func(v,_now(v,0),ppt)+v->bias[0]; |
---|
| 384 | float secondmetric=v->metric_func(v,_now(v,1),ppt)+v->bias[1]; |
---|
| 385 | long firstentry=0; |
---|
| 386 | long secondentry=1; |
---|
| 387 | |
---|
| 388 | if(!(i&0xff))spinnit("biasing... ",v->points+v->points+v->entries-i); |
---|
| 389 | |
---|
| 390 | if(firstmetric>secondmetric){ |
---|
| 391 | float temp=firstmetric; |
---|
| 392 | firstmetric=secondmetric; |
---|
| 393 | secondmetric=temp; |
---|
| 394 | firstentry=1; |
---|
| 395 | secondentry=0; |
---|
| 396 | } |
---|
| 397 | |
---|
| 398 | for(j=2;j<v->entries;j++){ |
---|
| 399 | float thismetric=v->metric_func(v,_now(v,j),ppt)+v->bias[j]; |
---|
| 400 | if(thismetric<secondmetric){ |
---|
| 401 | if(thismetric<firstmetric){ |
---|
| 402 | secondmetric=firstmetric; |
---|
| 403 | secondentry=firstentry; |
---|
| 404 | firstmetric=thismetric; |
---|
| 405 | firstentry=j; |
---|
| 406 | }else{ |
---|
| 407 | secondmetric=thismetric; |
---|
| 408 | secondentry=j; |
---|
| 409 | } |
---|
| 410 | } |
---|
| 411 | } |
---|
| 412 | |
---|
| 413 | j=firstentry; |
---|
| 414 | for(j=0;j<v->entries;j++){ |
---|
| 415 | |
---|
| 416 | float thismetric,localmetric; |
---|
| 417 | float *nearbiasptr=nearbias+desired2*j; |
---|
| 418 | long k=nearcount[j]; |
---|
| 419 | |
---|
| 420 | localmetric=v->metric_func(v,_now(v,j),ppt); |
---|
| 421 | /* 'thismetric' is to be the bias value necessary in the current |
---|
| 422 | arrangement for entry j to capture point i */ |
---|
| 423 | if(firstentry==j){ |
---|
| 424 | /* use the secondary entry as the threshhold */ |
---|
| 425 | thismetric=secondmetric-localmetric; |
---|
| 426 | }else{ |
---|
| 427 | /* use the primary entry as the threshhold */ |
---|
| 428 | thismetric=firstmetric-localmetric; |
---|
| 429 | } |
---|
| 430 | |
---|
| 431 | /* support the idea of 'minimum distance'... if we want the |
---|
| 432 | cells in a codebook to be roughly some minimum size (as with |
---|
| 433 | the low resolution residue books) */ |
---|
| 434 | |
---|
| 435 | /* a cute two-stage delayed sorting hack */ |
---|
| 436 | if(k<desired){ |
---|
| 437 | nearbiasptr[k]=thismetric; |
---|
| 438 | k++; |
---|
| 439 | if(k==desired){ |
---|
| 440 | spinnit("biasing... ",v->points+v->points+v->entries-i); |
---|
| 441 | qsort(nearbiasptr,desired,sizeof(float),directdsort); |
---|
| 442 | } |
---|
| 443 | |
---|
| 444 | }else if(thismetric>nearbiasptr[desired-1]){ |
---|
| 445 | nearbiasptr[k]=thismetric; |
---|
| 446 | k++; |
---|
| 447 | if(k==desired2){ |
---|
| 448 | spinnit("biasing... ",v->points+v->points+v->entries-i); |
---|
| 449 | qsort(nearbiasptr,desired2,sizeof(float),directdsort); |
---|
| 450 | k=desired; |
---|
| 451 | } |
---|
| 452 | } |
---|
| 453 | nearcount[j]=k; |
---|
| 454 | } |
---|
| 455 | } |
---|
| 456 | |
---|
| 457 | /* inflate/deflate */ |
---|
| 458 | |
---|
| 459 | for(i=0;i<v->entries;i++){ |
---|
| 460 | float *nearbiasptr=nearbias+desired2*i; |
---|
| 461 | |
---|
| 462 | spinnit("biasing... ",v->points+v->entries-i); |
---|
| 463 | |
---|
| 464 | /* due to the delayed sorting, we likely need to finish it off....*/ |
---|
| 465 | if(nearcount[i]>desired) |
---|
| 466 | qsort(nearbiasptr,nearcount[i],sizeof(float),directdsort); |
---|
| 467 | |
---|
| 468 | v->bias[i]=nearbiasptr[desired-1]; |
---|
| 469 | |
---|
| 470 | } |
---|
| 471 | }else{ |
---|
| 472 | memset(v->bias,0,v->entries*sizeof(float)); |
---|
| 473 | } |
---|
| 474 | |
---|
| 475 | /* Now assign with new bias and find new midpoints */ |
---|
| 476 | for(i=0;i<v->points;i++){ |
---|
| 477 | float *ppt=v->weight_func(v,_point(v,i)); |
---|
| 478 | float firstmetric=v->metric_func(v,_now(v,0),ppt)+v->bias[0]; |
---|
| 479 | long firstentry=0; |
---|
| 480 | |
---|
| 481 | if(!(i&0xff))spinnit("centering... ",v->points-i); |
---|
| 482 | |
---|
| 483 | for(j=0;j<v->entries;j++){ |
---|
| 484 | float thismetric=v->metric_func(v,_now(v,j),ppt)+v->bias[j]; |
---|
| 485 | if(thismetric<firstmetric){ |
---|
| 486 | firstmetric=thismetric; |
---|
| 487 | firstentry=j; |
---|
| 488 | } |
---|
| 489 | } |
---|
| 490 | |
---|
| 491 | j=firstentry; |
---|
| 492 | |
---|
| 493 | #ifdef NOISY |
---|
| 494 | fprintf(cells,"%g %g\n%g %g\n\n", |
---|
| 495 | _now(v,j)[0],_now(v,j)[1], |
---|
| 496 | ppt[0],ppt[1]); |
---|
| 497 | #endif |
---|
| 498 | |
---|
| 499 | firstmetric-=v->bias[j]; |
---|
| 500 | meterror+=firstmetric; |
---|
| 501 | |
---|
| 502 | if(v->centroid==0){ |
---|
| 503 | /* set up midpoints for next iter */ |
---|
| 504 | if(v->assigned[j]++){ |
---|
| 505 | for(k=0;k<v->elements;k++) |
---|
| 506 | vN(new,j)[k]+=ppt[k]; |
---|
| 507 | if(firstmetric>v->max[j])v->max[j]=firstmetric; |
---|
| 508 | }else{ |
---|
| 509 | for(k=0;k<v->elements;k++) |
---|
| 510 | vN(new,j)[k]=ppt[k]; |
---|
| 511 | v->max[j]=firstmetric; |
---|
| 512 | } |
---|
| 513 | }else{ |
---|
| 514 | /* centroid */ |
---|
| 515 | if(v->assigned[j]++){ |
---|
| 516 | for(k=0;k<v->elements;k++){ |
---|
| 517 | if(vN(new,j)[k]>ppt[k])vN(new,j)[k]=ppt[k]; |
---|
| 518 | if(vN(new2,j)[k]<ppt[k])vN(new2,j)[k]=ppt[k]; |
---|
| 519 | } |
---|
| 520 | if(firstmetric>v->max[firstentry])v->max[j]=firstmetric; |
---|
| 521 | }else{ |
---|
| 522 | for(k=0;k<v->elements;k++){ |
---|
| 523 | vN(new,j)[k]=ppt[k]; |
---|
| 524 | vN(new2,j)[k]=ppt[k]; |
---|
| 525 | } |
---|
| 526 | v->max[firstentry]=firstmetric; |
---|
| 527 | } |
---|
| 528 | } |
---|
| 529 | } |
---|
| 530 | |
---|
| 531 | /* assign midpoints */ |
---|
| 532 | |
---|
| 533 | for(j=0;j<v->entries;j++){ |
---|
| 534 | #ifdef NOISY |
---|
| 535 | fprintf(assig,"%ld\n",v->assigned[j]); |
---|
| 536 | fprintf(bias,"%g\n",v->bias[j]); |
---|
| 537 | #endif |
---|
| 538 | asserror+=fabs(v->assigned[j]-fdesired); |
---|
| 539 | if(v->assigned[j]){ |
---|
| 540 | if(v->centroid==0){ |
---|
| 541 | for(k=0;k<v->elements;k++) |
---|
| 542 | _now(v,j)[k]=vN(new,j)[k]/v->assigned[j]; |
---|
| 543 | }else{ |
---|
| 544 | for(k=0;k<v->elements;k++) |
---|
| 545 | _now(v,j)[k]=(vN(new,j)[k]+vN(new2,j)[k])/2.f; |
---|
| 546 | } |
---|
| 547 | } |
---|
| 548 | } |
---|
| 549 | |
---|
| 550 | asserror/=(v->entries*fdesired); |
---|
| 551 | |
---|
| 552 | fprintf(stderr,"Pass #%d... ",v->it); |
---|
| 553 | fprintf(stderr,": dist %g(%g) metric error=%g \n", |
---|
| 554 | asserror,fdesired,meterror/v->points); |
---|
| 555 | v->it++; |
---|
| 556 | |
---|
| 557 | free(new); |
---|
| 558 | free(nearcount); |
---|
| 559 | free(nearbias); |
---|
| 560 | #ifdef NOISY |
---|
| 561 | fclose(assig); |
---|
| 562 | fclose(bias); |
---|
| 563 | fclose(cells); |
---|
| 564 | #endif |
---|
| 565 | return(asserror); |
---|
| 566 | } |
---|
| 567 | |
---|