/***************************************************************************** * * * FOX Controller Library, Version 1.3.3. * * * * Copyright (C) 1998,1999,2000 Russell Smith (rl.smith@auckland.ac.nz) * * * * The FOX Controller Library is free software; you can redistribute it * * and/or modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either version * * 2 of the License, or (at your option) any later version. * * * * The FOX Controller Library is distributed in the hope that it will be * * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with the Fox Controller Library; see the file COPYING.LIB. * * If not, write to the Free Software Foundation, Inc., 59 Temple Place - * * Suite 330, Boston, MA 02111-1307, USA. * * * *****************************************************************************/ /* FoxN ---- There are two modes of operation: mapping and training. Training mode maintains eligibilities for each weight, but in mapping mode all the eligibilities are set to zero. In mapping mode you can call Map() repeatedly to map inputs to outputs. In training mode you must call Train() after each call to Map(). Training mode is the default. Technical note: We could arrange things so that in mapping mode the eligibilities were merely frozen at their current values, not set to zero. But this would add extra complexity, because when an inactive (buffered) weight is updated to its correct value its stored eligibility is no longer correct, but we have nowhere to put the accurate eligibility returned by GetCorrectWeight(). To maintain the correct eligibilities would require extra buffers, so we ignore the whole problem by setting all eligibilities to zero in mapping mode, i.e. we correct _all_ the weights when we make the transition into that mode. Thus: in mapping mode the inactive weight queue is always empty. LIMITATIONS: * Currently only one output is supported. */ #ifndef __FOX_H #define __FOX_H #include "cftype-n.h" #include "trace-n.h" #include "atoi.h" //*************************************************************************** // Use the slow method for debugging, i.e. by comparison with the fast method. #define FAST_METHOD 1 // #define SLOW_METHOD 1 //*************************************************************************** // CMAC // maximum allowed number of inputs #define CMAC_MAXIN 15 // maximum allowed number of association units #define CMAC_MAXC 100 // information provided about a CMAC input struct CmacInputInfo { int res; // resolution cftype min; // minimum input range cftype max; // maximum input range }; class FoxN { struct InputInfo { // internal information about an input int res; // resolution cftype min; // minimum input range cftype max; // maximum input range = min + res/q cftype q; // 1 / quantization size unsigned int parts; // maximum number of receptive field numbers for input }; int valid; // has this cmac been initialized? int nin; // number of inputs (<= CMAC_MAXIN) int C; // number of weights referenced by a single input InputInfo iinfo[CMAC_MAXIN]; // array, input information int wsize; // size of weights array / C cftype *weights; // weight array (one for each output) int *windex; // used by Train: array [C] of weight indexes char *odv; // overlay displacement vector int *aux; // weight auxiliary info array cftype output; // set by Map(), cmac output cftype old_output; // previous output int qin [CMAC_MAXIN]; // array of quantized inputs // eligibility stuff int ny; // size of eligibility vector: 1*ny cftype matA[MAX_NY*MAX_NY]; // constant A matrix, size = ny*ny cftype matB[MAX_NY]; // constant B matrix, size = ny*nx = ny*1 cftype matC[MAX_NY]; // constant C matrix, size = 1*ny GetAtoi *atoi; // calculate A^i and A^(-i) int time; // time (first time is 0) int elimode; // 0 = mapping mode, >0 = training mode. // 1 = between timesteps, 2 = just called Map(), the // next call must be to Train(). int history; // how many timesteps back to remember in buffer int bufsize1; // size of buffer 1 (ints) = history*C int bufsize2; // size of buffer 2 (cftypes) = history*C*ny int *buffer1; // eligibility buffer 1: weight indexes or -1 if none cftype *buffer2; // eligibility buffer 2: ny-sized eligibility vectors int head; // buffer head position TraceN *trace; cftype lasterror; // last error passed to TrainEli() #ifdef SLOW_METHOD cftype *slow_eli; // eligibility vectors, size = num weights * ny #endif public: FoxN (int num_inputs, CmacInputInfo *input_info, int num_weights, int iC, int _ny, cftype *_matA, cftype *_matB, cftype *_matC, int _history); // Initialize cmac for given number of inputs of given specifications. // num_weights = approximate number of weights in weight table (per // output), this will be rounded to a multiple of C. // iC = number of weights referenced by an input. // The A,B and C matrices define the eligibility filter response. // If initialization is unsuccessful in user mode a fatal error occurrs. // If initialization is unsuccessful in kernel mode then the constructor // returns and Valid() returns 0. The object can be safely destructed. ~FoxN(); int Valid() { return valid; } void SetEligibilityMode (int _elimode); // If `elimode' is nonzero then set training mode, else set mapping mode. void Map (cftype *input); // Map given input values to outputs. The windex array is set to the // indexes of the weights addressed by the association units. // The outputs can be accessed by FoxN[i]. // This advances the current time and processes the eligibilities of the // previously accessed weights. void MapGate (cftype *input, int gate); // Like Map() but the eligibility driving signal can be gated. void Train (cftype error); // For the current input / weight eligibilities, train the CMAC with the // given error signal. void LimitOutput (cftype lr); // Train the output to the target `0' without using eligibilities. // `lr' is the "learning rate". This must be called after a Map() so that // the weight indexes are correct and the indexed weights have their // correct values. void LimitOutputDerivative (cftype lr); // Like LimitOutput(), but the output derivative is limited instead. void UpperLimit (cftype limit); void LowerLimit (cftype limit); // Enforce the given upper/lower limits on the CMAC output. void SetOutput (cftype x); // Adjust the weights so the output has the given value. This is used // when multiple FOXs are used together to service multiple constraints // on a single output. void Reset(); // Reset time to zero, reset all eligibilities to zero, but the CMAC // weights are unchanged. void ResetAll(); // Like Reset() but all weights are cleared to 0 as well. int InRange (cftype *input); // Returns 1 if the input vector is in the input receptive field of the // cmac. The user may not choose to Map() inputs outside this range. // CMAC data interface cftype Output() { return output; } // access the output int NumWeights() { return wsize*C; } // number of weights / output int NumAUs() { return C; }; // number of association units cftype *Weights() { return weights; } // access weights array private: // utility functions // return the overlay offset (receptive field displacement, in the range // 0..C-1) for input 'inp' (0..nin-1) and association unit 'au' (0..C-1) int OverlayOffset (int inp, int au) { if (au==0) return 0; else return (int(odv[inp]) * au) % C; } void Quantize (cftype *input); // Quantize all 'nin' inputs (to their given input resolutions) into qin. int Association (int a); // Given that Quantize() has been called, return the weight index for // association unit 'a'. void GetAssociations(); // Given that Quantize() has been called, determine the weight indexes for // all association units. Put this info in array 'windex'. // Ensure that no two entries in `windex' are the same. const cftype * GetCorrectWeight (int i, int old); // Set weight index i to its correct value for all outputs --- if it is // in the inactive buffer then find its correct integrated value and // remove it from the buffer. // // If old==0, set the associated eligiblity to its correct value and // return a pointer to the updated eligibility vector. This vector will be // in a static storage area. If old==1 then the returned vector will be // undefined. // // If old==1 then the current buffer head position contains values from // bufsize1/C timesteps ago. This assumption is valid when weights are // being retired. If old==0 then the current buffer head position // contains current values. // // This should only ever be called from Map() and Reset(). }; //*************************************************************************** #endif