textfiles/computers/blum.lst

702 lines
18 KiB
Plaintext
Raw Normal View History

2021-04-15 11:31:59 -07:00
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;