TriuneCadence/THES/ANNCOMP.PP

1742 lines
52 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

PROGRAM Note_Generator (INPUT,OUTPUT);
{
This program demonstrates a small application which uses three
major neural network models to advantage in a cooperative,
integrated manner. The problem is that of generating musical
notes in a well-formed, but not repetitious manner. The scale of
notes is limited, and only one note is generated per time interval.
}
{
Copyright 1989 by Wesley R. Elsberry. All rights reserved.
Commercial use of this software is prohibited without written consent of
the author.
For information, bug reports, and updates contact
Wesley R. Elsberry
528 Chambers Creek Drive South
Everman, Texas 76140
Telephone: (817) 551-7018
}
USES
DOS, {Turbo Pascal MS-DOS functions}
CRT, {Turbo Pascal IBM-PC screen and keyboard functions}
ANN, {Artificial Neural Network functions}
Struct, {Linked-list structure functions}
Misc1, {Miscellaneous functions}
ANSI_Z, {ANSI screen control functions}
BP_unit, {Back-propagation network functions}
Globals, {Global types and constants}
ClasInst; {Classical Instructor function}
VAR
inf : TEXT; {Input file handle}
outf : TEXT; {Output file handle}
main_ii, {Loop variable}
main_jj : INTEGER; {Loop variable}
main_done : BOOLEAN; {Loop variable}
main_time : Time_rec_; {Record for time info}
Time1, Time2 : Time_rec_;
Testr : REAL;
Inchar : char;
note_rec : note_record_;
ii : INTEGER;
snet : BP_net_;
{For Beethoven (ART 1)}
F1 : F1_layer_ptr_;
F2 : F2_layer_ptr_;
{End VAR declarations}
{---------------------------------------------------------}
PROCEDURE Wait;
{}
BEGIN {}
{ Writeln('Press a key...');}
{ READ(inchar);}
END; {}
{----------------------------------------------------------}
PROCEDURE report_notes (VAR cmn : Common_area_);
TYPE
outstr_ = STRING[10];
VAR
ii : INTEGER;
raoutf : FILE OF CHAR;
raoutfname : STRING;
och : CHAR;
BEGIN
{open note file}
{skip to end}
{write note}
{close}
raoutfname := FSEARCH('BEETHOVN.MUS',GETENV('PATH'));
IF raoutfname <> '' THEN BEGIN
Assign(raoutf,raoutfname);
Reset(raoutf);
END
ELSE BEGIN
Assign(raoutf,'BEETHOVN.MUS');
Rewrite(raoutf);
END;
Seek (raoutf, FileSize(raoutf));
{go to end of file}
IF note_rec.c = 2 THEN BEGIN
och := ascii_cr;
Write(raoutf,och);
och := ascii_lf;
Write(raoutf,och);
och := ascii_asterisk;
Write(raoutf,och);
och := ascii_equal;
Write(raoutf,och);
och := ascii_asterisk;
Write(raoutf,och);
och := ascii_cr;
Write(raoutf,och);
och := ascii_lf;
Write(raoutf,och);
END;
och := Chr(note_rec.n[note_rec.c-1]+48);
Write(raoutf,och);
och := ascii_cr;
Write(raoutf,och);
och := ascii_lf;
Write(raoutf,och);
IF note_rec.c = 153 THEN BEGIN
och := ascii_cr;
Write(raoutf,och);
och := ascii_lf;
Write(raoutf,och);
och := ascii_asterisk;
Write(raoutf,och);
och := ascii_equal;
Write(raoutf,och);
och := ascii_asterisk;
Write(raoutf,och);
och := ascii_cr;
Write(raoutf,och);
och := ascii_lf;
Write(raoutf,och);
END;
Close(raoutf);
ANSI_CUP(2,0);
Write('Notes generated: ');
ANSI_CUP(2,25);
Write((note_rec.c-1):3);
END;
PROCEDURE record_a_note (VAR cmn : Common_area_);
CONST
Init : BOOLEAN = FALSE;
c : INTEGER = 1;
TYPE
outstr_ = STRING[10];
VAR
ii : INTEGER;
raoutf : FILE OF CHAR;
raoutfname : STRING;
och : CHAR;
BEGIN
IF NOT init THEN BEGIN
FillChar(note_rec.n,SizeOf(note_rec.n),#0);
note_rec.c := 1;
Init := TRUE;
END;
FOR ii := 1 TO V_len_out-1 DO BEGIN
{}
cmn.notes[ii] := cmn.notes[ii+1];
END; {}
cmn.notes[V_len_out] := 0;
note_rec.n[note_rec.c] := cmn.notes[v_len_out-1];
INC(note_rec.c);
report_notes(cmn);
END;
PROCEDURE play_a_note(VAR cn : BYTE);
BEGIN
CASE cn OF
1 : Sound(n_c_mid);
2 : Sound(n_d);
3 : Sound(n_e);
4 : Sound(n_f);
5 : Sound(n_g);
6 : Sound(n_a);
7 : Sound(n_b);
8 : Sound(n_c_hi);
ELSE
NoSound;
END;
Delay(180);
NoSound;
Delay(55);
END;
PROCEDURE play_notes (nr : note_record_);
VAR
ii : INTEGER;
BEGIN
ii := 1;
FOR ii := 1 TO nr.c DO play_a_note ( nr.n[ii]);
END;
PROCEDURE {Change_global_factors}
user_keys;
CONST
Initialized : BOOLEAN = FALSE;
VAR
inch : CHAR;
instr : STRING;
tempr : REAL;
err : INTEGER;
PROCEDURE display_global_factors;
BEGIN
ANSI_CUP(13,12);
Write('*res: ',HTN_co_res:5:4);
ANSI_CUP(14,12);
Write('*cap: ',HTN_co_cap:5:4);
ANSI_CUP(15,12);
Write(' *wt: ',HTN_co_wt:5:4);
ANSI_CUP(16,12);
Write('*inp: ',HTN_co_inp:5:4);
ANSI_CUP(17,12);
Write('epsi: ',epsilon:5:4);
ANSI_CUP(18,12);
Write('iter: ',HTN_co_iter:5:4);
ANSI_CUP(9,55);
Write('*Vigilance: ',ART_co_vigilance:5:4);
ANSI_CUP(23,0);
END;
BEGIN
IF NOT Initialized THEN BEGIN
ANSI_CUP(23,0);
Write
('Type "C" to change factors, "P" to play notes so far.');
display_global_factors;
ANSI_CUP(23,0);
Initialized := TRUE;
END; {IF NOT Initialized}
IF check_kbd_status THEN BEGIN
IF dir_console_IO(inch) THEN BEGIN
inch := UpCase(inch);
IF inch = 'C' THEN BEGIN
ANSI_CUP(21,0);
Write(
'Change: 1)epsi 2)*res 3)*cap 4)*wt 5)*inp 6)iter 7)*vigilance'
);
REPEAT
WHILE (NOT dir_console_IO(inch)) DO ;
UNTIL (inch IN ['1','2','3','4','5','6','7']);
REPEAT
ANSI_CUP(21,0);
ANSI_EEOL;
ANSI_CUP(21,0);
Write('Input value: ');
Readln(instr);
Val(instr,tempr,ii);
UNTIL (ii = 0);
CASE inch OF
'1' : epsilon := tempr;
'2' : HTN_co_res := tempr;
'3' : HTN_co_cap := tempr;
'4' : HTN_co_wt := tempr;
'5' : HTN_co_inp := tempr;
'6' : HTN_co_iter := tempr;
'7' : ART_co_vigilance := tempr;
END;
ANSI_CUP(22,0);
ANSI_EEOL;
display_global_factors;
ANSI_CUP(23,0);
END
ELSE IF inch = 'P' THEN BEGIN
play_notes(note_rec);
END; {Else if inch}
END;
END;
END;
{----------------------------------------------------------}
PROCEDURE Bach(VAR cmn : Common_Area_);
{Generates a new note from past sequence and frequency information.
Uses a Hopfield-Tank network to accomplish this task. }
{INPUTS:
Sequence of notes, 4 notes long
OUTPUT:
Single note, valued from 1 to 8
}
{Hopfield-Tank network. Given data and input values,
processes for output.}
PROCEDURE HTN(VAR cma : Common_Area_);
CONST
Initialized : BOOLEAN = FALSE;
TYPE
W_A_ptr_ = ^Weight_Array_;
weight_array_ = ARRAY[1..64,1..64] OF REAL;
file_string_ = STRING[127];
neuron_ = RECORD
a : REAL; {activation value}
r : REAL; {resistance}
c : REAL; {capacitance}
o : REAL; {output}
i : REAL; {input}
END;
neuron_array_ = ARRAY[1..v_len_in,1..v_len_out] OF neuron_;
note_array_ = ARRAY[1..5] OF INTEGER;
CONST
WA : W_A_ptr_ = NIL;
VAR
inf : FILE OF weight_array_;
Time_step : INTEGER;
ns : ARRAY[0..1] OF neuron_array_;
ii, jj, kk : INTEGER;
nbase, nindex : INTEGER;
FUNCTION Neuron_Output(act,cap :REAL):REAL;
BEGIN
neuron_output := 0.5 *(1 + tanh(act/cap));
END;
FUNCTION max_cell_in_column(col : INTEGER):INTEGER;
VAR
ii, jj : INTEGER;
Hi : REAL;
nsptr : INTEGER;
BEGIN {max_cell_in_column}
Hi := 0.0;
nsptr := time_step MOD 2;
FOR ii := 1 TO v_len_in DO BEGIN
IF (ns[nsptr,ii,col].o > Hi) THEN BEGIN
Hi := ns[nsptr,ii,col].o;
jj := ii;
END;
END;
max_cell_in_column := jj;
END; {max_cell_in_column}
FUNCTION done(epsi : REAL):BOOLEAN;
VAR
finish : BOOLEAN;
ii, jj : INTEGER;
t : REAL;
BEGIN
finish := FALSE;
ii := 1;
WHILE (NOT finish) AND (ii <= V_len_in) DO BEGIN
FOR jj := 1 TO 5 DO BEGIN
IF ABS(ns[0,ii,jj].o - ns[1,ii,jj].o) > epsi THEN
finish := TRUE;
END;
ii := ii + 1;
END;
done := NOT finish;
END;
FUNCTION Convert_to_weight_coord(note,posit : INTEGER):INTEGER;
BEGIN {Convert_to_weight_coord}
Convert_to_weight_coord := (v_len_in*(posit-1)+note);
END; {Convert_to_weight_coord}
FUNCTION delta_neuron_activation(a,r,i : REAL;
note,posit : INTEGER):REAL;
VAR
ii, jj : INTEGER;
tempr1, tempr2 : REAL;
sum : REAL;
di1,di2 : INTEGER;
current : INTEGER;
BEGIN {delta_neuron_activation}
current := time_step MOD 2;
sum := 0.0;
di1 := Convert_to_weight_coord(note,posit);
FOR ii := 1 TO v_len_in DO
FOR jj := 1 TO v_len_out DO BEGIN
di2 := Convert_to_weight_coord(ii,jj);
sum := sum
+ (WA^[di1,di2] * HTN_co_wt)
* ns[current,ii,jj].o;
END;
delta_neuron_activation :=
(-(ns[current,note,posit].a
/ (ns[current,note,posit].r * HTN_co_res))
+ (ns[current,note,posit].i * HTN_co_inp) + sum)
/ (ns[current,note,posit].c * HTN_co_cap);
END; {delta_neuron_activation}
FUNCTION iterate_htn(VAR nts : notes_):INTEGER;
VAR
II, JJ, next_time : INTEGER;
PROCEDURE display_neuron_activation;
CONST
column = 0;
row = 10;
VAR
ii, jj : INTEGER;
ts : INTEGER;
active : INTEGER;
BEGIN {display_neuron_activation}
ts := time_step;
FOR ii := 1 TO v_len_in DO BEGIN
{FOR ii}
ANSI_CUP(row+ii,0);
FOR jj := 1 TO v_len_out DO BEGIN
{FOR jj}
active := Round(ns[ts,ii,jj].o * 10);
IF active > 10 THEN active := 10;
IF active < 1 THEN active := 1;
Write(Copy(graphic_string,active,1),' ');
END; {FOR jj}
END; {FOR ii}
ANSI_CUP(23,0);
END; {display_neuron_activation}
PROCEDURE update_neuron_output;
VAR
ii, jj : INTEGER;
BEGIN
FOR ii := 1 TO v_len_in DO BEGIN
{FOR ii}
FOR jj := 1 TO v_len_out DO BEGIN
{FOR jj}
ns[time_step,ii,jj].o :=
neuron_output(ns[time_step,ii,jj].a,
ns[time_step,ii,jj].c);
END; {FOR jj}
END; {FOR ii}
END;
BEGIN {iterate_HTn}
time_step := 0;
{initialize the neuron arrays}
FOR ii := 1 TO 8 DO
FOR jj := 1 TO 5 DO BEGIN
ns[0,ii,jj].a := 0.5;
ns[0,ii,jj].o := 0.0;
ns[1,ii,jj].o := 0.0;
IF (nts[jj] = 0) THEN BEGIN
{randomize input}
ns[0,ii,jj].i := gaussian_noise(0.5,0.25);
ns[1,ii,jj].i := ns[0,ii,jj].i;
END
ELSE BEGIN
IF (nts[jj] = ii) THEN BEGIN
ns[0,ii,jj].i := 0.67
+ gaussian_noise(0.0,0.1);
ns[1,ii,jj].i := ns[0,ii,jj].i;
END
ELSE BEGIN
ns[0,ii,jj].i := 0.33
+ gaussian_noise(0.0,0.1);
ns[1,ii,jj].i := ns[0,ii,jj].i;
END;
END;
END;
{prevent premature end}
ns[1,1,1].o := 20;
update_neuron_output;
WHILE (NOT done(epsilon)) DO BEGIN
user_keys;
time_step := time_step MOD 2;
next_time := (time_step + 1) MOD 2;
{determine output term for neurons}
update_neuron_output;
FOR ii := 1 TO v_len_in DO BEGIN
{FOR ii}
FOR jj := 1 TO v_len_out DO BEGIN
{FOR jj}
ns[next_time,ii,jj].a := ns[time_step,ii,jj].a
+ HTN_co_iter
* delta_neuron_activation(ns[time_step,
ii,jj].a, ns[time_step,ii,jj].r,
ns[time_step,ii,jj].i,ii,jj);
END; {FOR jj}
END; {FOR ii}
display_neuron_activation;
time_step := time_step + 1;
END; {WHILE}
{**Change of note: Finding the highest activity in each column and
setting it for further processing will change the behavior of the
net and program. The change is being made to improve performance of
the ART1 network.**}
FOR ii := 1 TO v_len_out DO BEGIN
nts[ii] := max_cell_in_column(ii);
END;
iterate_htn := nts[v_len_out];
END; {Iterate_HTn}
BEGIN {HTn}
{ Initialized := False;}
IF (NOT Initialized) THEN{do initialize}
BEGIN {get weight matrix}
New(WA);
Assign(inf,'htn.dat');
Reset(inf);
Read(inf,WA^);
Close(inf);
Initialized := TRUE;
ANSI_CUP(8,0);
Write('HTN:');
ANSI_CUP(9,0);
Write('Node activity');
ANSI_CUP(11,12);
Write('R: ',global_resistance:5:3);
ANSI_CUP(12,12);
Write('C: ',global_capacitance:5:3);
ANSI_CUP(23,0);
END;
FOR ii := 1 TO 8 DO
FOR jj := 1 TO 5 DO BEGIN
ns[0,ii,jj].r := global_resistance;
ns[0,ii,jj].c := global_capacitance;
ns[1,ii,jj].r := global_resistance;
ns[1,ii,jj].c := global_capacitance;
END;
FOR ii := 1 TO v_len_out DO
{clear notes}
cma.notes[ii] := 0;
nbase := note_rec.c - v_len_out;
FOR nindex := 1 TO (v_len_out - 1) DO BEGIN
IF ((nbase + nindex) > 0) THEN
cma.notes[nindex] := note_rec.n[(nbase+nindex)];
END; {For}
cma.Candidate_note := iterate_htn(cma.notes);
cma.notes[v_len_out] := cma.candidate_note;
END; {HTn}
BEGIN {Bach}
Dump_Common(cmn);
HTn(cmn);
END; {Bach}
{----------------------------------------------------------}
PROCEDURE Salieri(VAR cmn : Common_Area_);
{Compares past information and proposed note generated by Bach with
rules of classical composition. A PDP network is used to do this.}
PROCEDURE Back_propagation(VAR cmn : Common_Area_);
{A PDP style back propagation network.}
CONST
Initialized : BOOLEAN = FALSE;
count : WORD = 1;
TYPE
bpnp_ = BP_node_ptr_;
wnp_ = weight_node_ptr_;
vnp_ = vector_node_ptr_;
VAR
ii, jj, kk : INTEGER;
Done : BOOLEAN;
tp1 : DVE_ptr_;
error_m, tne, sum : ARRAY[1..3] OF REAL;
ss : STRING;
binsum : ARRAY[1..3] OF INTEGER;
FUNCTION max (r1, r2 :REAL):REAL;
BEGIN
IF r1 >= r2 THEN max := r1
ELSE max := r2;
END;
PROCEDURE Set_input_vector_from_notes (vp : DVE_ptr_;
n : notes_);
VAR
ii : INTEGER;
vpt : DVE_ptr_;
vn : ARRAY[1..40] OF INTEGER;
BEGIN
FillChar (vn,SizeOf(vn),#0);
{Blank the current vector}
FOR ii := 1 TO 5 DO BEGIN
{Notes subscript}
IF n[ii] > 0 THEN vn [((ii-1)*8)+n[ii]] := 1;
END; {For notes subscript}
vpt := vp;
FOR ii := 1 TO snet.n_input DO BEGIN
vnp_(vpt^.dptr)^.v := vn[ii];
vpt := vpt^.right;
END; {FOR ii}
END;
BEGIN
IF NOT Initialized THEN BEGIN
snet.data_fname := 's61.dat';
ANSI_CUP(15,40);
Setup_BP_net (snet,snet.data_fname);
Write(snet.data_fname);
ANSI_CUP(16,40);
Set_BP_net_weights_from_file(snet,snet.wt_fname);
Write(snet.wt_fname);
Initialized := TRUE;
END;
{now set up current input vector}
Set_input_vector_from_notes(snet.vi,cmn.notes);
{get the supervisor's critique for the current sequence}
vnp_(snet.vts^.dptr)^.v := Classical_instructor (cmn.notes);
{Feed-forward, back-propagate, and make changes}
BP_train_and_change (snet);
{determine response of the net to the current sequence}
cmn.Is_classical := (vnp_(snet.vos^.dptr)^.v > 0.50);
{keep weight changes that have been made now and then}
IF (count MOD 49) = 0 THEN
Dump_BP_net_weights(snet,snet.wt_fname);
INC(count);
END;
BEGIN {Salieri}
Dump_common(cmn);
Back_propagation(cmn);
END; {Salieri}
{----------------------------------------------------------}
PROCEDURE Beethoven(VAR cmn : Common_Area_);
{}
PROCEDURE ART1(VAR cma : Common_Area_);
{Binary Adaptive Resonance Theory model}
{
Carpenter and Grossberg's ART1 paradigm
Copyright 1988, W.R. Elsberry & D.J. Blackwood
Written in Turbo Pascal 5.5, September 1988
}
{
Terminology:
STM = Short Term Memory
LTM = Long Term Memory
TD = Top-down
BU = Bottom-up
F1 layer = a vector of nodes which receive input from three sources:
External input, a binary vector
Gain control, an internal processing input
F2 layer, activation of category nodes through LTM traces
F2 layer = a set of nodes which have LTM memory traces associated
with them that relate to the BU activations to particular nodes in
the F2 level.
Inputs: F1 activation through BU LTM traces, Gain Control,
System Reset
}
{
Parameter constraints from Carpenter & Grossberg, 1987
A >= 0
mu1, mu2 >= 0 (Simpson)
C >= 0
epsilon1, epsilon2 >= 0 (Simpson)
MAX(1,D) < B < 1 + D
MAX(1,gamma1) < sigma1 < (1 + gamma1) (Simpson)
0 < e << 1
K = O(1)
L > 1
0 < p <= 1
0 < Vigilance <= 1 (Simpson)
0 < Z_IJ(0) < (L / (L-1+M))
0 < Wup(0) < (L / (L - 1 + Max_F1_nodes)) (Simpson)
1 >= Z_JI(0) > Z_BAR == ((B-1)/D)
1 >= Wdn(0) > ? == ((sigma2-1)/gamma2) (Simpson)
0 <= I,f,g,h <= 1
}
CONST
Initialized : BOOLEAN = FALSE;
Initial_Wup = 0.1;
Initial_Wdn = 0.9;
Number_committed_F2 : INTEGER = 0;
Vigilance : REAL = 0.9; {Determines what level of mismatch will
cause reset}
Time_slice : REAL = 0.1; {Factor to multiply deltas by}
{The following are part of the F1 STM recall equation}
mu1 : REAL = 1; {Positive constant controlling BU input & TD
feedback}
sigma1 : REAL = 1.4; {Positive constant regulating gain control}
epsilon1 : REAL = 1; {Positive constant regulating gain control}
gamma1 : REAL = 1; {Positive constant regulating TD and BU
feedback}
{The following are part of the F2 STM recall equation}
mu2 : REAL = 1; {Positive constant controlling BU input & TD
feedback}
sigma2 : REAL = 1.4; {Positive constant regulating gain control}
epsilon2 : REAL = 1; {Positive constant regulating gain control}
gamma2 : REAL = 1; {Positive constant regulating BU input}
{The following are part of the Bottom-Up LTM equation}
alpha1 : REAL = 1; {Positive constant for learning rate}
beta1 : REAL = 1; {Positive constant for passive decay}
{The following are part of the Top-Down LTM equation}
alpha2 : REAL = 1; {Positive constant for learning rate}
beta2 : REAL = 1; {Positive constant for passive decay}
VAR
i, j : INTEGER;
F2_winner : INTEGER; {Index of winning F2 node}
Input_on : BOOLEAN; {Is input currently being received?}
Resonance : BOOLEAN;
{The following are part of the match operation equation}
Vector_I : Vector_; {Binary input vector}
Vector_X : Vector_; {Binary expected vector}
PROCEDURE Build_Expected_Vector;
{}
CONST
low = 0.0;
high = 1.0;
{thresh = 0.5;}
VAR
ii, jj : INTEGER;
thresh : REAL;
BEGIN {Build_Expected_Vector}
thresh := 0.0;
WITH F2^[F2_winner] DO BEGIN
{with}
FOR ii := 1 TO Max_F1_nodes DO BEGIN
{}
thresh := thresh + Wdn[ii];
END; {}
thresh := thresh / Vector_length;
FOR ii := 1 TO Max_F1_nodes DO BEGIN
Vector_X[ii] := BYTE(Round (Threshold(low, high, thresh,
Wdn[ii]) ));
END;
END; {with}
END; {Build_Expected_Vector}
PROCEDURE Build_Input_Vector;
{}
VAR
ii, jj : INTEGER;
FUNCTION One_if_NZ(This, That : INTEGER) :INTEGER;
{}
VAR
ii : INTEGER;
BEGIN {}
ii := This AND That;
IF (ii <> 0) THEN {}
BEGIN
ii := 1;
END;
One_if_NZ := ii;
END; {}
BEGIN {Build_Input_Vector}
FillChar (vector_i,SizeOf(vector_i),#0);
{Blank the current vector}
FOR ii := 1 TO 5 DO BEGIN
{Notes subscript}
IF cma.notes[ii] > 0 THEN
vector_i [((ii-1)*8)+cma.notes[ii]] := 1;
END; {For notes subscript}
IF (cma.Is_classical) THEN
{}
BEGIN
Vector_I[Max_F1_nodes] := 1;
END
ELSE {}
BEGIN
Vector_I[Max_F1_nodes] := 0;
END;
END; {Build_Input_Vector}
PROCEDURE Display_vectors;
{}
VAR
vii : INTEGER;
BEGIN {}
ANSI_CUP(8,28);
Write('ART1: # Committed F2: ',Number_committed_F2,
' Vigilance: ',vigilance:5:4);
ANSI_CUP(9,37);
Write('F2 Winner : ',F2_winner:3);
ANSI_CUP(10,28);
Write('Expected Vector vs. Input Vector');
ANSI_CUP(12,28);
Write('IV: ');
FOR vii := 1 TO vector_length DO BEGIN
{}
IF ((vii MOD 8) = 1) AND (vii <> 1) THEN Write(' ');
Write(Vector_I[vii]);
END; {}
ANSI_CUP(11,28);
Build_Expected_Vector;
Write('EV: ');
FOR vii := 1 TO vector_length DO BEGIN
{}
IF ((vii MOD 8) = 1) AND (vii <> 1) THEN Write(' ');
Write(Vector_X[vii]);
END; {}
END; {}
PROCEDURE Clear_ART1;
{}
VAR
ii, jj : INTEGER;
BEGIN {Clear_ART1}
FOR ii := 1 TO Max_F2_nodes DO BEGIN
{Clear F2 node parameters}
WITH F2^[ii] DO BEGIN
Eligible := TRUE;
Curr_B := 0;
Last_B := 0;
END;
END; {Clear F2 node parameters}
FOR ii := 1 TO Max_F1_nodes DO BEGIN
{Clear F1 node parameters}
WITH F1^[ii] DO BEGIN
Curr_A := 0;
Last_A := 0;
END;
END; {Clear F1 node parameters}
F2_winner := 0;
END; {Clear_ART1}
PROCEDURE Initialize_ART1;
{}
VAR
ii, jj, kk : INTEGER;
BEGIN {Initialize_ART1}
Number_committed_F2 := 0;
New(F2);
New(F1);
FOR kk := 1 TO Max_F2_nodes DO BEGIN
{FOR}
WITH F2^[kk] DO BEGIN
{WITH}
Committed := FALSE;
FOR ii := 1 TO Vector_Length DO BEGIN
{For}
Wup[ii] := Initial_Wup;
Wdn[ii] := Initial_Wdn;
Last_B := 0.0;
Curr_B := 0.0;
END; {For}
END; {WITH}
END; {FOR}
FOR kk := 1 TO Max_F1_nodes DO BEGIN
{}
WITH F1^[kk] DO BEGIN
{}
Last_A := 0.0;
Curr_A := 0.0;
END; {}
END; {}
END; {Initialize_ART1}
FUNCTION Delta_STM_F1_node(nde : INTEGER):REAL;
{
Simpson (1988) Eq. 19
a_dot[nde] = - a[nde]
+ (1 - mu1 * a[nde]) *
(gamma1 * F2[f2_winner].wdn[nde] + Input[nde])
- (sigma1 + epsilon1 * a(nde) * (1 if there is a winner)
(0 otherwise)
t1 = - a[nde]
t2 = + (1 - mu1 * a[nde])
t3 = (gamma1 * F2[f2_winner].wdn[nde] + Input[nde])
t4 = (sigma1 + epsilon1 * a(nde)
t5 = (1 if there is a winner)
(0 otherwise)
so,
Delta_STM_F1_node := t1 + t2*t3 - t4*t5;
}
VAR
ii, jj : INTEGER;
t1, t2, t3, t4, t5 : REAL;
BEGIN {Delta_STM_F1_node}
t1 := 0;
t2 := 0;
t3 := 0;
t4 := 0;
t5 := 0;
WITH F1^[nde] DO BEGIN
t1 := - Last_A;
t2 := (1 - mu1*Last_A);
IF (F2_winner <> 0) THEN
{}
BEGIN {Make TD term}
t3 := F2^[F2_winner].Wdn[nde];
END;
t3 := (gamma1*t3 + Vector_I[nde]);
t4 := (sigma1 + epsilon1*Last_A);
IF (F2_winner > 0) THEN
{}
BEGIN
t5 := 1;
END
ELSE {}
BEGIN
t5 := 0;
END;
END;
Delta_STM_F1_node := t1 + t2*t3 - t4*t5;
END; {Delta_STM_F1_node}
FUNCTION Delta_STM_F2_node(nde : INTEGER):REAL;
{
Simpson (1988) Eq. 20
b_dot[nde] = - b[nde]
+ (1 - mu2 * b[nde])b*
(gamma2 * [sum over i of S(a[i] * F2[i].wup[nde] ]
+ f(b[nde])
- (sigma2 + epsilon2 * b(nde) *
[sum over k<>j of S(b[k]) ]
where,
t1 = - b[nde]
t2 = (1 - mu2 * b[nde])
t3 = (gamma2 * [sum over i of S(a[i] * F2[i].wup[nde] ] + f(b[nde])
t4 = (sigma2 + epsilon2 * b(nde)
t5 = [sum over k<>j of S(b[k]) ]
Delta_STM_F2_node := t1 + t2*t3 - t4*t5;
}
CONST
range = 1;
slope_mod = 1;
shift = 0;
VAR
ii, jj : INTEGER;
t1, t2, t3, t4, t5 : REAL;
BEGIN {Delta_STM_F2_node}
t1 := 0;
t2 := 0;
t3 := 0;
t4 := 0;
t5 := 0;
WITH F2^[nde] DO BEGIN
t1 := - Last_B;
t2 := (1 - mu2*Last_B);
FOR ii := 1 TO Max_F1_nodes DO BEGIN
{Make TD term}
t3 := t3 + sigmoid(1,1,0,F1^[ii].Curr_A)*Wup[ii];
END; {Make TD term}
IF (nde = F2_winner) THEN{}
BEGIN
jj := 1;
END
ELSE {}
BEGIN
jj := 0;
END;
t3 := (gamma2*t3 + jj);
t4 := (sigma2 + epsilon2*Last_B);
FOR ii := 1 TO number_Committed_F2 DO BEGIN
{for}
IF (Eligible) THEN BEGIN
IF (ii <> nde) THEN
t5 := t5 + Sigmoid(range, slope_mod, shift,
F2^[ii].Last_B);
END;
END; {for}
END;
Delta_STM_F2_node := t1 + t2*t3 - t4*t5;
END; {Delta_STM_F2_node}
FUNCTION Delta_LTM_Bottom_Up(F2_nde, F1_nde : INTEGER):REAL;
{
Simpson (1988) Eq. 16
wup_dot(ij) = alpha1 * f(b[j]) * (-beta1 * wup[ij] + S(a[i]) )
This corrects an error in the text!
}
CONST
range = 1;
slope_mod = 1;
shift = 0;
VAR
ii, jj : INTEGER;
t1, t2, t3,t4, t5 : REAL;
BEGIN {Delta_LTM_Bottom_Up}
IF (F2_winner = F2_nde) THEN{}
BEGIN
WITH F2^[F2_nde] DO BEGIN{}
Delta_LTM_Bottom_Up := alpha1 * ( -beta1 * Wup[F1_nde]
+ Sigmoid (range, slope_mod, shift,
F1^[F1_nde].curr_A) );
END;
END
ELSE BEGIN
Delta_LTM_Bottom_Up := 0;
END;
END; {Delta_LTM_Bottom_Up}
FUNCTION Delta_LTM_Top_Down(F2_nde, F1_nde : INTEGER):REAL;
{
Simpson (1988) Eq. 17
wdn_dot(ji) = alpha2 * f(b[j]) * (-beta2 * wdn[ji] + S(a[i]) )
This also corrects an error in the text!
}
CONST
range = 1;
slope_mod = 1;
shift = 0;
VAR
ii, jj : INTEGER;
t1, t2, t3,t4, t5 : REAL;
BEGIN {Delta_LTM_Top_Down}
IF (F2_winner = F2_nde) THEN{}
BEGIN
WITH F2^[F2_nde] DO BEGIN{}
Delta_LTM_Top_Down :=
alpha2 * ( -beta2 * Wdn[F1_nde] + Sigmoid(
range, slope_mod, shift, F1^[F1_nde].curr_A) );
END; {}
END
ELSE BEGIN
Delta_LTM_Top_Down := 0;
END;
END; {Delta_LTM_Top_Down}
FUNCTION Raw_match:INTEGER;
{
Result of bitwise AND of Vector_I and Vector_X
}
VAR
ii, jj : INTEGER;
BEGIN {Raw_match}
jj := 0;
FOR ii := 1 TO Vector_length DO BEGIN
{}
IF (Vector_I[ii] = 1) AND (Vector_X[ii] = 1) THEN
{}
BEGIN
jj := jj + 1;
END
ELSE {}
BEGIN
END;
END; {}
Raw_match := jj;
END; {Raw_match}
FUNCTION Ones_in_Vector_I:REAL;
{}
VAR
ii, jj : INTEGER;
BEGIN {Ones_in_Vector_I}
jj := 0;
FOR ii := 1 TO Vector_Length DO BEGIN
{}
jj := jj + Vector_I[ii];
END; {}
Ones_in_Vector_I := jj;
END; {Ones_in_Vector_I}
FUNCTION Match:BOOLEAN;
{
Return TRUE if Match between I and X exceeds vigilance
}
VAR
RM, OVI : REAL;
BEGIN {Match}
RM := Raw_Match;
OVI := Ones_in_Vector_I;
IF (OVI = 0) THEN {}
BEGIN
IF (RM > 0) THEN {}
BEGIN
Match := TRUE;
END
ELSE {}
BEGIN
Match := FALSE;
END;
END
ELSE {}
BEGIN
Match := ((RM/OVI) >= (Vigilance * ART_co_vigilance));
END;
END; {Match}
FUNCTION Saturated:BOOLEAN;
{}
BEGIN {Saturated}
Saturated := (Number_committed_F2 >= Max_F2_nodes);
END; {Saturated}
FUNCTION Find_F2_winner : INTEGER;
{}
VAR
ii, jj : INTEGER;
max_value : REAL;
Max_position : INTEGER;
Min_num : INTEGER;
BEGIN {Find_F2_winner}
{Find the maximum activation}
max_value := -1.0E38;
MAX_POSITION := 1;
IF (Number_Committed_F2 < Max_F2_nodes) THEN
{}
BEGIN
Min_num := Number_Committed_F2;
END
ELSE {}
BEGIN
Min_num := Max_F2_nodes;
END;
FOR jj := 1 TO Min_Num DO BEGIN
{FOR}
WITH F2^[jj] DO BEGIN {WITH}
IF (Eligible) AND (Committed) THEN BEGIN
{Eligible AND Committed}
IF (Curr_B > max_value) THEN BEGIN
{Current > Max}
max_value := Curr_B;
max_position := jj;
END; {Current > Max}
END; {Eligible AND Committed}
END; {WITH}
END; {FOR}
IF (Number_committed_F2 > 0) THEN
{}
BEGIN
Find_F2_winner := max_position;
ANSI_CUP(8,28);
Write
('ART1: # Committed F2: ',Number_committed_F2,
' Vigilance: ',vigilance:5:4);
ANSI_CUP(9,37);
Write('F2 Winner : ',max_position:3);
ANSI_CUP(23,0);
wait;
END
ELSE {}
BEGIN
Find_F2_winner := 0;
ANSI_CUP(9,37);
Write('F2 Winner : ',0:3);
ANSI_CUP(23,0);
END;
END; {Find_F2_winner}
FUNCTION Changed_STM_F1(epsilon : REAL) : BOOLEAN;
{}
VAR
ii, jj : INTEGER;
Temp : BOOLEAN;
rr : REAL;
BEGIN {Changed_STM_F1}
Temp := FALSE;
FOR II := 1 TO Vector_Length DO BEGIN
{For}
WITH F1^[ii] DO BEGIN {With}
rr := ABS(Curr_A - Last_A);
IF (rr > epsilon) THEN{Changed}
BEGIN
Temp := TRUE;
END;
END; {With}
END; {For}
Changed_STM_F1 := Temp;
END; {Changed_STM_F1}
FUNCTION Changed_STM_F2 (epsilon : REAL): BOOLEAN;
{}
VAR
ii, jj : INTEGER;
Temp : BOOLEAN;
rr : REAL;
BEGIN {Changed_STM_F2}
Temp := FALSE;
IF (F2_winner > 0) AND (F2_winner <= Max_F2_nodes) THEN BEGIN
WITH F2^[F2_winner] DO BEGIN
{With}
rr := ABS(Curr_B - Last_B);
IF (rr > epsilon) THEN{Changed}
BEGIN
Temp := TRUE;
END;
END; {With}
END
ELSE BEGIN
Temp := FALSE;
END;
Changed_STM_F2 := Temp;
END; {Changed_STM_F2}
PROCEDURE Do_resonate;
{}
CONST
Iter = 10;
E = 0.05;
VAR
ii, jj, End_loop : INTEGER;
BEGIN {Do_resonate}
End_loop := 0;
{While change in STM do alternate BU and TD STM and BU and TD LTM}
REPEAT {}
{BU STM}
FOR ii := 1 TO Vector_Length DO BEGIN
{For F1 STM}
WITH F1^[ii] DO BEGIN {With F1}
Last_A := Curr_A;
Curr_A := Last_A + time_slice * Delta_STM_F1_node(ii);
END; {With F1}
END; {For F1 STM}
{BU LTM}
FOR ii := 1 TO Vector_Length DO BEGIN
{For BU LTM}
WITH F2^[F2_winner] DO BEGIN
{With F2_winner}
Wup[ii] := Wup[ii] + time_slice
* Delta_LTM_Bottom_up(F2_winner,ii);
END; {With F2_winner}
END; {For BU LTM}
{ Display_vectors;}
{TD STM}
WITH F2^[F2_winner] DO BEGIN
{}
Last_B := Curr_B;
Curr_B := Last_B + time_slice
* Delta_STM_F2_node(F2_winner);
END; {}
{TD LTM}
FOR ii := 1 TO Vector_Length DO BEGIN
{For TD LTM}
WITH F2^[F2_winner] DO BEGIN
{With F2_winner}
Wdn[ii] := Wdn[ii] + time_slice
* Delta_LTM_Top_Down(F2_winner,ii);
END; {With F2_winner}
END; {For TD LTM}
{ Display_vectors;}
End_Loop := End_Loop + 1;
UNTIL ((NOT Changed_STM_F1(e)) AND (NOT Changed_STM_F2(e)))
OR (End_loop > Iter); {}
Display_vectors;
Resonance := TRUE;
END; {Do_resonate}
FUNCTION Exists_eligible : BOOLEAN;
{}
VAR
ii, jj : INTEGER;
Temp : BOOLEAN;
BEGIN {Exists_eligible}
Temp := FALSE;
FOR jj := 1 TO Number_committed_F2 DO BEGIN
{FOR}
WITH F2^[jj] DO BEGIN {WITH}
IF (Eligible) AND (Committed) THEN BEGIN
{Eligible AND Committed}
Temp := TRUE;
END; {Eligible AND Committed}
END; {WITH}
END; {FOR}
Exists_eligible := Temp;
END; {Exists_eligible}
PROCEDURE Encode_New_F2;
{}
VAR
ii, jj : INTEGER;
BEGIN {}
IF number_committed_F2 < Max_F2_nodes THEN BEGIN
{Find next uncommitted F2 node}
F2_winner := Number_committed_F2 + 1;
{Resonate uncommitted F2 node with F1}
Do_resonate;
F2^[F2_winner].Eligible := TRUE;
F2^[F2_winner].Committed := TRUE;
Number_committed_F2 := Number_committed_F2 + 1;
END; {IF }
END; {}
PROCEDURE Find_F1_activation;
VAR
i, j : INTEGER;
BEGIN
ANSI_CUP(14,37);
Write('Find F1 Activation':30);
ANSI_CUP(23,0);
FOR j := 1 TO 3 DO
FOR i := 1 TO Vector_Length DO BEGIN
{FOR}
WITH F1^[i] DO BEGIN {WITH}
Last_A := Curr_A;
Curr_A := Last_A + time_slice * Delta_STM_F1_Node(i);
END; {WITH}
END; {FOR}
END;
PROCEDURE Find_F2_activation;
VAR
i, J : INTEGER;
BEGIN
ANSI_CUP(14,37);
Write('Find F2 Activation':30);
ANSI_CUP(23,0);
FOR j := 1 TO Number_committed_F2 DO BEGIN
{FOR}
WITH F2^[j] DO BEGIN {WITH}
IF (Eligible) AND (Committed) THEN
{}
BEGIN
Last_B := Curr_B;
Curr_B := Last_B + time_slice * Delta_STM_F2_node(j);
END;
END; {WITH}
END; {FOR}
END;
BEGIN {ART1}
ANSI_CUP(8,28);
Write('ART1:');
ANSI_CUP(14,28);
Write('Process:');
ANSI_CUP(23,0);
Build_Input_vector;
IF (NOT Initialized) THEN BEGIN {do initialize}
ANSI_CUP(14,37);
Write('Initialize':30);
ANSI_CUP(23,0);
Initialize_ART1;
Initialized := TRUE;
END;
{Clear_ART}
ANSI_CUP(14,37);
Write('Clear':30);
ANSI_CUP(23,0);
Clear_ART1;
cma.Delta_Vigilance := FALSE;
cma.New_Category := FALSE;
REPEAT
user_keys;
Resonance := FALSE;
{Find current F1 activation}
{F1_i node activation * Wij}
Find_F1_activation;
{Send F1 activation to F2}
Find_F2_activation;
{If no committed F2 nodes, then
proceed to new encoding}
IF (NOT exists_eligible) OR (number_committed_F2 = 0) THEN
{}
BEGIN
ANSI_CUP(14,37);
Write('No comm. F2, encoding':30);
ANSI_CUP(23,0);
Encode_New_F2;
Resonance := TRUE;
cma.New_Category := TRUE;
END
ELSE BEGIN
{F2 competition}
{Determine maximum of
eligible F2 nodes}
ANSI_CUP(14,37);
Write('F2 Competition':30);
ANSI_CUP(23,0);
F2_winner := Find_F2_winner;
{F2 winner sends TD image back to F1}
{Activation of F2 winner * Wji}
Display_vectors; {Makes a call to Build_Expected_Vector}
{Compare Input vector to F2 TD vector}
IF (Match) THEN BEGIN {Resonate}
ANSI_CUP(14,37);
Write('Matched, now resonate':30);
ANSI_CUP(23,0);
Resonance := TRUE;
Do_resonate;
END {Resonate}
ELSE BEGIN {Mismatch}
ANSI_CUP(14,37);
Write('Mismatch':30);
ANSI_CUP(23,0);
{Make the F2 node ineligible}
F2^[F2_winner].Eligible := FALSE;
{Have we saturated?}
{Yes, lower vigilance and restart}
{ * While this is not part of the
Carpenter-Grossberg ART 1 architecture,
this modification we felt necessary for
the small number of category nodes which
we are using in the model. * }
IF (Saturated) AND (NOT exists_eligible) THEN
{If saturation reached decrease vigilance and
restart}
BEGIN {Saturation}
ANSI_CUP(14,37);
Write('Saturated, vigilance--':30);
ANSI_CUP(23,0); {vigilance is decreased}
Vigilance := Vigilance * 0.99;
ANSI_CUP(8,57);
Write('Vigilance: ',vigilance:5:4);
ANSI_CUP(23,0);
cma.Delta_Vigilance := TRUE;
{call clear_ART1}
Clear_ART1;
Find_F1_activation;
Find_F2_activation;{now find closest match and resonate}
F2_winner := Find_F2_winner;
Resonance := TRUE;
Do_resonate;
END {Saturation}
ELSE BEGIN {Not saturated or exists_eligible}
{Are there eligible F2 nodes?}
IF (Exists_eligible) THEN
{Yes, go on with current process}
BEGIN
ANSI_CUP(14,37);
Write('Search Eligible F2':30);
ANSI_CUP(23,0); {Just continue}
END
ELSE BEGIN
{No, form a new encoding if not saturated}
IF NOT saturated THEN BEGIN
ANSI_CUP(14,37);
Write('Encode new category':30);
ANSI_CUP(23,0);
Encode_New_F2;
Resonance := TRUE;
cma.New_Category := TRUE;
END; {IF NOT saturated}
END;
END; {Not saturated}
END; {Mismatch}
END; {ELSE}
UNTIL (Resonance);
{Prep info to pass back}
END; {ART1}
{----------------------------------------------------}
BEGIN {Beethoven}
Dump_common(cmn);
ART1(cmn);
END; {Beethoven}
{----------------------------------------------------------}
PROCEDURE Lobes;
{
Keeps track of played notes, maintaining sequence information.
Uses data from Beethoven to determine when to override Salieri.
}
CONST
Max_notes_in_composition = 152;
Object_threshold = 3;
Frustration_threshold = 10;
VAR
Common : Common_Area_;
Number_notes : INTEGER; {Note counter}
Objects : BOOLEAN;
Note_Played : BOOLEAN;
Generate_Candidate : BOOLEAN;
Need_Critique : BOOLEAN;
Need_Compose : BOOLEAN;
Since_Novelty, Frustration : INTEGER;
ii : INTEGER;
BEGIN {Lobes}
Randomize;
note_rec.c := 0;
Since_Novelty := 0;
Frustration := 0;
Common.notes[1] := 0;
Common.notes[2] := 0;
Common.notes[3] := 0;
Common.notes[4] := 0;
Common.notes[5] := 0;
Common.delta_vigilance := FALSE;
Common.new_category := FALSE;
Common.candidate_note := 0;
Common.is_classical := FALSE;
ANSI_CUP(14,37);
Write('Begin Simulation':30);
ANSI_CUP(23,0);
FOR Number_notes := 1 TO Max_notes_in_composition DO BEGIN
{}
user_keys;
Note_played := FALSE;
Generate_Candidate := TRUE;
Need_Critique := TRUE;
Need_Compose := TRUE;
REPEAT
IF Generate_candidate THEN
{Generate a candidate note, HTn}
BEGIN
Bach(Common);
ANSI_CUP(6,0);
Write('Candidate Note:');
ANSI_CUP(6,35);
Write(common.candidate_note);
ANSI_CUP(23,0);
END;
wait;
IF Need_Critique THEN BEGIN
{Find if it is a candidate sequence, PDP}
Salieri(Common);
ANSI_CUP(5,0);
Write('Candidate sequence classical?:');
ANSI_CUP(5,31);
Write(common.Is_Classical);
ANSI_CUP(23,0);
END;
wait;
IF Need_Compose THEN {Pass through ART and }
BEGIN
Beethoven(Common);
END;
wait; {IF Delta_vigilance or New_category,
then zero the count}
{Else increment the count}
IF (Common.Delta_vigilance OR Common.New_Category) THEN
{}
BEGIN
Since_Novelty := 0;
END
ELSE {}
BEGIN
Since_novelty := Since_Novelty + 1;
END;
IF (Common.Delta_vigilance) THEN BEGIN
INC(Frustration);
END;
{If count >= Object_threshold), then Objects
is true, reset count}
{Else Objects is false}
Objects := (Since_Novelty >= Object_threshold);
{OR (Frustration > Frustration_Threshold);}
IF Objects THEN since_novelty := 0;
IF (objects AND common.is_classical) OR ((NOT objects)
AND (NOT common.is_classical)) THEN BEGIN
generate_candidate := TRUE;
need_critique := TRUE;
need_compose := TRUE;
common.notes[v_len_out] := 0;
INC(Frustration);
END
ELSE BEGIN
record_a_note(Common);
note_played := TRUE;
Frustration := 0;
END;
UNTIL (Note_played); {A note has been played}
END; {}
END; {Lobes}
{----------------------------------------------------------}
BEGIN {Main}
WRITELN('Copyright 1989 by Wesley R. Elsberry');
DELAY(2000);
ANSI_CLRSCR;
Lobes;
END. {Main}