home *** CD-ROM | disk | FTP | other *** search
- 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<stdlib.h>
- #include<io.h>
- #include<stdio.h>
- #include<string.h>
- #include<limits.h>
- #include<ctype.h>
- #include<stream.hpp>
- #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<n;i++)
- v[i]=0;
- } // constructor
- vec::~vec() { delete v;} // destructor
- vec::vec(vec& v1) // copy-initializer
- {
- v=new int[n=v1.n];
- for(int i=0;i<n;i++)
- v[i]=v1.v[i];
- }
- vec& vec::operator=(const vec& v1)
- {
- delete v;
- v=new int[n=v1.n];
- for(int i=0;i<n;i++)
- v[i]=v1.v[i];
- return *this;
- }
- vec& vec::operator+(const vec& v1)
- {
- vec sum(v1.n);
- sum.n=v1.n;
- for(int i=0;i<v1.n;i++)
- sum.v[i]=v1.v[i]+v[i];
- return sum;
- }
- vec& vec::operator+=(const vec& v1)
- {
- for(int i=0;i<v1.n;i++)
- v[i]+=v1.v[i];
- return *this;
- }
- vec vec::operator*(int c)
- {
- vec prod(length());
- for(int i=0;i<prod.n;i++)
- prod.v[i]=v[i]*c;
- return prod;
- }
- int vec::operator*(const vec& v1) // dot-product
- {
- int sum=0;
- for(int i=0;i<min(n,v1.n);i++)
- sum+=(v1.v[i]*v[i]);
- //D(cout << "dot product " << *this << v1 << sum << "\n";)
- return sum;
- }
- int vec::operator==(const vec& v1)
- {
- if(v1.n!=n)return 0;
- for(int i=0;i<min(n,v1.n);i++){
- if(v1.v[i]!=v[i]){
- return 0;
- }
- }
- return 1;
- }
- int& vec::operator[](int x)
- {
- if(x<length() && x>=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<v.n;i++)
- s << (v.v[i]<0?0:1);
- s << ",";
- return s;
- }
-
- ///////////////////////////////
- // matrix member functions
- matrix::matrix(int n,int p)
- {
- //D(cout << "Constructing " << n << " x " << p << " matrix.\n";)
- m=new int *[n];
- for(int i=0;i<n;i++){
- m[i]=new int[p];
- for(int j=0;j<p;j++)
- m[i][j]=0;
- }
- r=n;
- c=p;
- } // constructor
- matrix::matrix(const vecpair& vp)
- {
- //D(cout << "Constructing matrix from: " << vp;)
- r=vp.a.length();
- c=vp.b.length();
- m=new int *[r];
- for(int i=0;i<r;i++){
- m[i]=new int[c];
- for(int j=0;j<c;j++)
- m[i][j]=vp.a.v[i]*vp.b.v[j];
- }
- }// constructor
- matrix::matrix(const vec& v1,const vec& v2)
- {
- //D(cout << "Constructing matrix from " << v1 << v2 << "\n";)
- r=v1.length();
- c=v2.length();
- m=new int *[r];
- for(int i=0;i<r;i++){
- m[i]=new int[c];
- for(int j=0;j<c;j++)
- m[i][j]=v1.v[i]*v2.v[j];
- }
- }// constructor
- matrix::matrix(matrix& m1) // copy-initializer
- {
- //D(cout << "matrix copy-initializer\n"; )
- r=m1.r;
- c=m1.c;
- m=new int *[r];
- for(int i=0;i<r;i++){
- m[i]=new int[c];
- for(int j=0;j<c;j++)
- m[i][j]=m1.m[i][j];
- }
- }
- matrix::~matrix()
- {
- for(int i=0;i<r;i++)
- delete m[i];
- delete m;
- } // destructor
- matrix& matrix::operator=(const matrix& m1)
- {
- for(int i=0;i<r;i++)
- delete m[i];
- r=m1.r;
- c=m1.c;
- m=new int*[r];
- for(i=0;i<r;i++){
- m[i]=new int[c];
- for(int j=0;j<r;j++)
- m[i][j]=m1.m[i][j];
- }
- return *this;
- }
- matrix& matrix::operator+(const matrix& m1)
- {
- matrix sum(r,c);
- for(int i=0;i<r;i++)
- for(int j=0;j<r;j++)
- sum.m[i][j]=m1.m[i][j]+m[i][j];
- return sum;
- }
- matrix& matrix::operator+=(const matrix& m1)
- {
- //D(cout << "matrix additive assignment\n";)
- for(int i=0;i<r&&i<m1.r;i++)
- for(int j=0;j<c&&j<m1.c;j++)
- m[i][j]+=(m1.m[i][j]);
- return *this;
- }
- vec matrix::colslice(int col)
- {
- vec temp(r);
- for(int i=0;i<r;i++)
- temp.v[i]=m[i][col];
- return temp;
- }
- vec matrix::rowslice(int row)
- {
- vec temp(c);
- for(int i=0;i<c;i++)
- temp.v[i]=m[row][i];
- return temp;
- }
- int matrix::depth(){return r;}
- int matrix::width(){return c;}
-
- ostream& operator<<(ostream& s,matrix& m1)
- // print a matrix
- {
- for(int i=0;i<m1.r;i++){
- for(int j=0;j<m1.c;j++)
- s << m1.m[i][j] << " ";
- s << "\n";
- }
- }
- //////////////////////////////////////////
- // vecpair member functions
- // constructor
- vecpair::vecpair(int n,int p) { }
- vecpair::vecpair(const vec& A,const vec& B) {a=A;b=B;}
- vecpair::vecpair(const vecpair& AB) {*this=vecpair(AB.a,AB.b);}
- vecpair::~vecpair() {} // destructor
- vecpair& vecpair::operator=(const vecpair& v1)
- {
- a=v1.a;
- b=v1.b;
- return *this;
- }
- int vecpair::operator==(const vecpair& v1)
- {
- return (a == v1.a) && (b == v1.b);
- }
- istream& operator>>(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<<v1.a<<v1.b<<"\n";
- }
- /////////////////////////////////
- //bam_matrix member functions
- bam_matrix::bam_matrix(int n,int p):(n,p)
- {
- // the maximum number of pattern pairs storable
- // is around min(n,p) where n and p are
- // the dimensionality of the matrix
- C=new vecpair[min(n,p)*2];
- K=0;
- }
- bam_matrix::~bam_matrix()
- {
- } // destructor
- void bam_matrix::encode(const vecpair& AB)
- // encode a pattern pair
- {
- //D(cout << "BAM Matrix encoding: " << AB;)
- matrix T(AB);
- (*this)+=T; // add the matrix transpose to the current matrix
- C[K]=AB;
- K++;
- }
- void bam_matrix::uncode(const vecpair& AB)
- // get rid of a stored pattern (by encoding A-B complement)
- {
- //D(cout << "uncode\n";)
- vec v=AB.b*-1;
- matrix T(AB.a,v); // T is A transpose B complement
- *this+=T;// add the matrix transpose to the current matrix
- K--;
- }
- vecpair bam_matrix::recall(const vec& A)
- // BAM Matrix recall algorithm (used by BAM SYSTEM recall)
- {
- int givenrow=(A.length()==width());
- D(cout<<"BAM matrix recall of" << A << givenrow?"(row)\n":"(col)\n";)
- vec B(givenrow?depth():width(),1);
- for(;;){ // feed vectors through matrix until "resonant" pattern-pair
- feedthru(A,B);
- if(feedthru(B,A))break; // stop when returned A = input A
- }
- D(cout<< "resonant pair " << A << "\n and " << B << "\n";)
- if(givenrow)
- return vecpair(B,A);
- else
- return vecpair(A,B);
- }
- int bam_matrix::feedthru(const vec&A,vec& B)
- {
- //D(cout << "Feeding " << A << "\n"; )
- vec temp=B;int n;
- for(int i=0;i<B.length();i++){
- if(A.length()==width())
- n=sigmoid(A*rowslice(i));
- else
- n=sigmoid(A*colslice(i));
- if(n)
- B.v[i]=n;
- }
- return B==temp;
- }
- int bam_matrix::sigmoid(int n)
- // VERY simple (but classic one for BAM) threshold function
- //
- // 1 --------------
- // |
- // - ----------- +
- // -1
- {
- if(n<0)return -1;
- if(n>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;i<K;i++){
- AB=recall(C[i].a);
- if(!(AB==C[i])){
- D(cout <<"failed check\n ";)
- return 0;
- }
- }
- D(cout << "passed check\n ";)
- return 1;
- }
- int bam_matrix::check(const vecpair& AB)
- {
- // different check routine for orthogonal construction BAM
- //check to see energy of present pattern pair to matrix
- // is equal to orthogonal BAM energy
- matrix T(AB);
- return energy(T)== -depth()*width();
- }
- int bam_matrix::energy(const matrix& m1)
- {
- int sum=0;
- for(int i=0;i<depth();i++)
- for(int j=0;j<width();j++)
- sum+=(m1.m[i][j]*this->m[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;i<M;i++)
- W[i]=new bam_matrix;
- }
- bam_system::~bam_system() // destructor
- {
- for(int i=0;i<M;i++)
- delete W[i];
- }
- void bam_system::encode(const vecpair& AB)
- // encode the pattern pair AB into the BAOM system
- {
- D(cout << "BAM System encode\n";)
- for(int h=0;h<M;h++){
- W[h]->encode(AB);
- if(!W[h]->check())
- W[h]->uncode(AB);
- else
- break;
- }
- if(h==M){ // all matrices full, add another
- if(h<MAXMATS){
- W[M]=new bam_matrix();
- W[M]->encode(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;h<M;h++){
- XY[h]=W[h]->recall(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<b.M;i++)
- s<< "BAM Matrix " << i << ": \n" << *(b.W[i]) << "\n";
- }
-
-
- [LISTING THREE]
-
- ////////////////////////
- // TESTBAM.HPP
- // Interactive BAM System Demonstration Program. Used to verify BAM system
- // algorithms and demonstrate them on an abstract (i.e. just 0s and 1s) case.
- // Developed with Zortech C++ 2.0 -- Copyright (c) 1989,90 Adam Blum
-
- #include"bam.hpp"
-
- vec v;
- vecpair AB;
- bam_system B;
- char *p;
- char patternfile[16]="TEST.FIL"; // file where test data is stored
- int trace=0; // SET TRACE=<whatever> 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;
-
-
-
-
-
-
-