BIDIRECTIONAL ASSOCIATIVE MEMORY SYSTEMS IN C++ by Adam Blum [LISTING ONE] //////////////////////////////////////////////////////////// // BAM.HPP Provide vector, matrix, vector pair, matrix, BAM matrix, and // BAM system classes and methods to implement BAM system concept. // Extended note: // This is an implementation of the concept of Bidirectional // Associative Memories as developed by Bart Kosko and others. // It includes the extended concept introduced by Patrick Simpson // of the "BAM System". Where reasonable Simpson's notation has been // been maintained. The presentation benefits greatly from C++ and OOP, in that // (I believe) it is both easier to understand than a "pseudocode" version, // yet more precise (in that it works!) // Developed with Zortech C++ Version 2.0 -- Copyright (c) Adam Blum, 1989,90 #include #include #include #include #include #include #include #include "debug.h" // debugging devices // where are Zortech's min,max? #define max(a,b) (((a) > (b)) ? (a) : (b)) #define min(a,b) (((a) < (b)) ? (a) : (b)) // will be changed to much higher than these values const ROWS=16; // number of rows (length of first pattern) const COLS=8; // number of columns (length of second pattern) const MAXMATS=10; // maximum number of matrices in BAM system const MAXVEC=16; // default size of vectors class matrix; class bam_matrix; class vec { friend class matrix; friend class bam_matrix; friend class bam_system; int n; int *v; public: // see BAM.CPP for implementations of these vec(int size=MAXVEC,int val=0); // constructor ~vec(); // destructor vec(vec &v1); // copy-initializer int length(); vec& operator=(const vec& v1); // vector assignment vec& operator+(const vec& v1); // vector addition vec& operator+=(const vec& v1); // vector additive-assignment vec& operator*=(int i); // vector multiply by constant // supplied for completeness, but we don't use this now int operator*(const vec& v1); // dot product vec operator*(int c); // multiply by constant // vector transpose multiply needs access to v array int operator==(const vec& v1); int& operator[](int x); friend istream& operator>>(istream& s,vec& v); friend ostream& operator<<(ostream& s, vec& v); }; //vector class class vecpair; class matrix { protected: // bam_matrix (a derived class) will need to use these members // preferred to "friend class", since there may be many derived // classes which need to use this int **m; // the matrix representation int r,c; // number of rows and columns public: // constructors matrix(int n=ROWS,int p=COLS); matrix(const vec& v1,const vec& v2); matrix(const vecpair& vp); matrix(matrix& m1); // copy-initializer ~matrix(); int depth(); int width(); matrix& operator=(const matrix& m1); matrix& operator+(const matrix& m1); matrix& operator+=(const matrix& m1); vec colslice(int col); vec rowslice(int row); friend ostream& operator<<(ostream& s,matrix& m1); }; // matrix class class vecpair { friend class matrix; friend class bam_matrix; friend class bam_system; int flag; // flag signalling whether encoding succeeded vec a; vec b; public: vecpair(int n=ROWS,int p=COLS); // constructor vecpair(const vec& A,const vec& B); vecpair(const vecpair& AB); // copy initializer ~vecpair(); vecpair& operator=(const vecpair& v1); int operator==(const vecpair& v1); friend istream& operator>>(istream& s,vecpair& v); friend ostream& operator<<(ostream& s,vecpair& v); friend matrix::matrix(const vecpair& vp); }; class bam_matrix: public matrix { private: int K; // number of patterns stored in matrix vecpair *C; // actual pattern pairs stored int feedthru(const vec&A,vec& B); int sigmoid(int n); // sigmoid threshold function public: bam_matrix(int n=ROWS,int p=COLS); ~bam_matrix(); // but we supply it with the actual matrix A|B (W is implied) void encode(const vecpair& AB); // self-ref version // uncode only necessary for BAM-system void uncode(const vecpair& AB); // self-ref version vecpair recall(const vec& A); int check(); int check(const vecpair& AB); // Lyapunov energy function: E=-AWBtranspose int energy(const matrix& m1); // Lyapunov energy function }; // BAM matrix class bam_system { bam_matrix *W[MAXMATS]; int M; // number of matrices public: bam_system(int M=1); ~bam_system(); void encode(const vecpair& AB); vecpair& recall(const vec& A); // train equiv. to Simpson's encode of all pairs void train(char *patternfile); friend ostream& operator<<(ostream& s,bam_system& b); }; // BAM system class [LISTING TWO] /////////////////////////////////////// // BAM.CPP Provide vector, matrix, vector pair, matrix, BAM matrix, and BAM // system classes to implement BAM systems // Extended note: // This is an implementation of the concept of Bidirectional // Associative Memories as developed by Bart Kosko and others. // It includes the extended concept introduced by Patrick Simpson // of the "BAM System". Where reasonable Simpson's notation has been // been maintained. The presentation benefits greatly from C++ and OOP, in that // (I believe) it is both easier to understand than a "pseudocode" version, // yet more precise (in that it works!) // Developed with Zortech C++ Version 2.0 -- Copyright (c) 1989,90 Adam Blum #include"bam.hpp" /////////////////////////////////// // vector class member functions vec::vec(int size,int val) { v = new int[size]; n=size; for(int i=0;i=0) return v[x]; else cout << "vec index out of range"; } int vec::length(){return n;} // length method istream& operator>>(istream& s,vec &v) // format: list of ints followed by ',' { char c; v.n=0; v.v=new int[MAXVEC]; for(;;){ s>>c; if(s.eof())return s; if(c==',')return s; if(isspace(c))continue; v.v[v.n++]=((c!='0')?1:-1); } } ostream& operator<<(ostream& s, vec& v) // format: list of ints followed by ',' { for(int i=0;i>(istream& s,vecpair& v1) // input a vector pair { s>>v1.a>>v1.b; return s; } ostream& operator<<(ostream& s,vecpair& v1) // print a vector pair { return s<0)return 1; return 0; } int bam_matrix::check() // check to see if we have successfully encoded pattern-pair into this matrix { D(cout << "Check BAM matrix for " << K << " pattern pairs\n";) vecpair AB; for(int i=0;im[i][j]); D(cout << "Energy of matrix " << -sum << "\n";) return -sum; } /////////////////////////////////////////// // bam system functions // top level of system (for now) // constructor bam_system::bam_system(int n) { M=n; for(int i=0;iencode(AB); if(!W[h]->check()) W[h]->uncode(AB); else break; } if(h==M){ // all matrices full, add another if(hencode(AB); M++; } else{ cout << "BAM System full\n"; exit(1); } } } vecpair& bam_system::recall(const vec& A) // presented with pattern A, recall will return pattern-PAIR { vecpair XY[MAXMATS];matrix *M1,*M2; int E,minimum=0,emin=INT_MAX; D(cout << "BAM System recall\n";) for(int h=0;hrecall(A); D(cout << h <<"-th matrix, returned vecpair "<< XY[h];) M1=new matrix(XY[h]); E=W[h]->energy(*M1); if(A.length()==W[h]->width()) M2=new matrix(XY[h].a,A); else M2=new matrix(A,XY[h].b); if ( ( E-(W[h]->depth()*W[h]->width()) < emin ) && (E==W[h]->energy(*M2)) ) { emin=E-(W[h]->depth()*W[h]->width()); minimum=h; } delete M1; delete M2; } return XY[minimum]; } void bam_system::train(char *patternfile) // A "multiple-pair" encode - which Simpson calls "encode" // this could be used for initial BAM Sys training. However an up // and running BAM Sys should only need to use "encode". { FILE *f=fopen(patternfile,"r");int n=0; filebuf sfile(f); istream s(&sfile,0); vecpair AB; for(;;){ s >> AB; if(s.eof())break; D(cout << "Encoding " << n++ << "-th pattern pair:\n" << AB;) encode(AB); } D(cout << "Completed training from " << patternfile;) } ostream& operator<<(ostream& s,bam_system& b) // operator to print out contents of entire BAM system { for(int i=0;i at DOS prompt to turn trace on main() { cout << "Interactive BAM System Demonstration\n"; trace=(p=getenv("TRACE"))?1:0; cout << "Training from " << patternfile << "\n"; B.train(patternfile); D(cout << "Resulting BAM System\n" << B;) cout <<"Enter patterns as 0's and 1's terminated by comma.\n" <<"Patterns must be length of " << ROWS << " or " << COLS <<".\n" << "Null vector (just "","") to end.\n\n" ; for(;;){ cout << "Enter pattern: "; cin >> v; if(!v.length())break; if(v.length()!=ROWS && v.length()!=COLS){ cout << "Wrong length.\n"; continue; } AB=B.recall(v); cout << "Recalled pattern pair\n" << AB; } } [LISTING FOUR] 1100101011010011,11101010, 0110110111110110,11010101, 1101111001010101,11110010, 1010101000010111,11001101, 0011001101011011,11110100, 1100101011010011,11101010, 0110100111110110,11010101, 1101110101010101,11110010, 1011101010010111,11001101, 0001011101011011,11110100, 1100101001010011,11101010, 0110110110110110,11010101, 1100111011010101,11110011, 1010000100010111,11001101, 0001101101011011,11110110, 1100100011010011,11100110, 0110110011110110,11010101, 1101111001010101,11110011, 1010100000011111,11001101, 0001100101111011,11111000, 1100101011010011,11011010, 0010100111110110,11010101, 1101111101010101,11110010, 1010111000010111,11101101, 0001000001011011,11110100, 1100101011010011,11101010, 0110110111110110,11010101, 1101111000010101,11110110, 1010100111010111,11001101, 0001000101011011,11110100, 0110110101110110,11010111, 1101111001010101,11110110, 1010111100110111,11001101, 0001000101011011,11110100, 1100101010010011,11101010, 0110110111110110,11010101, 1101111001010101,11110010, 1010110000010111,11001101, 0011000101011011,11110100, 0011010101111011,10010111, [LISTING FIVE] # TESTBAM.MK # Make file for BAM System implementation tester # Uses Microsoft Make # Compiler: Zortech C++ 2.0 # To make with diagnostics enabled: # make CFLAGS="-DDEBUG=1" testbam.mk # CFLAGS= .cpp.obj: ztc -c $(CFLAGS) $*.cpp bam.obj: bam.cpp bam.hpp testbam.obj: testbam.cpp bam.hpp testbam.exe: testbam.obj bam.obj blink testbam bam;