'Program BRAIN.BAS is an artificial neural network architecture like the brain
'(c) 1998 by J. C. Sprott
'Compile with PowerBASIC 3.5
'March 19, 1998 version
defext a-z 'Use extended (80-bit) precision
cls
d0=1e-8 'Perturbation for LE calculation
input"Number of neurons";n%: if n%=0 then n%=100
mmax%=(fre(-1)-17000-40*n%)/(6*n%)-1 'Check available memory
if n%*mmax%>16383! then mmax%=int(16383!/n%) 'Arrays must fit in 64K
print"Maximum number of inputs is";mmax%
input"Number of inputs to each neuron";m%: if m%=0 then m%=10
input"Scale factor on weights";s: if s=0 then s=2
n$="Random"
screen 12
option base 1 'Start arrays from 1, not 0
dim x(n%),y(n%),xe(n%),ye(n%),inn%(n%,m%),w!(n%,m%)
randomize timer 'Reseed the random number generator
open"brainle.dat" for output as #2
do
cls
locate 1,64: print n$;" Neighbors";
t%=0
for i%=1 to n%
y(i%)=1-2*rnd 'Initial conditions
ye(i%)=y(i%)+d0/sqr(n%) 'Perturbed orbit
for j%=1 to m%
if n$="Random" then
inn%(i%,j%)=rnd(1,n%) 'Input connections
else
inn%(i%,j%)=1+(2*n%-m%\2+i%+j%-1) mod n%
end if
next j%
for j%=1 to m%
w!(i%,j%)=0
for k%=1 to 12 'Make Gaussian random deviates
w!(i%,j%)=w!(i%,j%)+rnd
next k%
w!(i%,j%)=(w!(i%,j%)-6)/sqr(m%) 'Input weights
next j%
next i%
lsum=0: iter&=0
while q$<>chr$(27) 'Iterate net until Esc key is pressed
incr iter%
q$=inkey$
if len(q$) then 'A key was pressed
select case ucase$(q$)
case chr$(13): exit loop 'Display a new network
case " ": d%=(d%+1) mod 5: cls 'Toggle display mode
case chr$(0,72)
s=1.1##*s
locate 1,1: print using"s =##.###^^^^";s
lsum=0: iter&=0
case chr$(0,80)
s=s/1.1##
locate 1,1: print using"s =##.###^^^^";s
lsum=0: iter&=0
case "N"
if n$="Near" then n$="Random" else n$="Near"
exit loop
case else 'Display menu of commands
cls
print "Commands:": print
print "","Display a new network"
print "","Toggle display mode"
print "","Increase s by 10%"
print "","Decrease s by 10%"
print "","Exit program"
print "N","Toggle neighborhood (now ";n$;")"
do: q$=inkey$: loop until len(q$): cls: iterate
end select
end if
for i%=1 to n%
x(i%)=y(i%) 'Update input vector
next i%
for i%=1 to n% 'Iterate the network equations
u=0
for j%=1 to m% 'This is the innermost (slowest) loop
jnn%=inn%(i%,j%)
u=u+w!(i%,j%)*x(jnn%)
next j%
y(i%)=phi(s*u) 'Calculate new output vector
next i%
dsum=0
for i%=1 to n% 'Do everything again to get the LE
xe(i%)=ye(i%) 'Update input vector
next i%
for i%=1 to n% 'Iterate the network equations
u=0
for j%=1 to m% 'This is the innermost (slowest) loop
jnn%=inn%(i%,j%)
u=u+w!(i%,j%)*xe(jnn%)
next j%
ye(i%)=phi(s*u) 'Calculate new output vector
dy=ye(i%)-y(i%)
dsum=dsum+dy*dy
next i%
d1=sqr(dsum)
for i%=1 to n% 'Readjust ye for separation d0
ye(i%)=y(i%)+(d0/d1)*(ye(i%)-y(i%))
next i%
lsum=lsum+log(d1/d0)
incr iter&
le=lsum/iter& 'Here's the LE
locate 30,1: print using"LE =##.####";le;
if iter%=1250 then
print#2, le
iter%=0
exit loop
end if
t%=(t%+1) mod 640
if d%=0 then 'Plot EEG (6 random neurons)
line(t%,0)-(t%,479),0
for i%=1 to min(6,n%)
line(t%-1,80*i%-40+30*x(i%))-(t%,80*i%-40+30*y(i%)),i%+9
next i%
elseif d%=1 then 'Plot all neurons with color code
for i%=1 to 480
pset(t%-1,i%-1),7.5*(y(int(i%*n%/480+1))+1)
next i%
elseif d%=2 then 'Plot Poincare section
pset(320+320*x(1),240-240*x(2))
elseif d%=3 then 'Plot average of all neurons
line(t%,0)-(t%,479),0
xav=0: yav=0
for i%=1 to n%: xav=xav+x(i%): yav=yav+y(i%): next i%
line(t%-1,240-240*xav/n%)-(t%,240-240*yav/n%)
elseif d%=4 then 'Plot directed graph of network
call dirgraph(inn%()): screen 12: d%=0
end if
wend
loop until q$=chr$(27)
end
FUNCTION phi(u)
'Returns hyperbolic tangent of u (squashing function)
phi=1-2/(exp(2*u)+1)
END FUNCTION
SUB dirgraph (inn%())
'Makes a directed graph of n% nodes each with m% inputs in the array inn%
n%=ubound(inn%,1): m%=ubound(inn%,2)
if n%>2020 then exit sub 'Maximum number of nodes is 2020
dim x%(2020), y%(2020), xm%(2020), ym%(2020)
indx%=0
for j%=38 to 0 step -1: for i%=51 to 0 step -1
incr indx%
if indx%>2020 then exit for
xm%(indx%)=14+12*i%: ym%(indx%)=12+12*j%
next i%,j%
for i%=1 to 2020 'Shuffle nodes
io%=1+int(2020*rnd)
swap xm%(i%),xm%(io%)
swap ym%(i%),ym%(io%)
next i%
screen 11
q$=""
maxpixels&=0
temp!=1
mintemp!=1/n% 'Minimum temperature allowed
xtmax!=-638/log(mintemp!)
cf!=exp(-1/xtmax!) 'Cooling factor
maxpix&=2232+29*n% 'Maximum number of pixels
maxpixels&=-4000000
minpix&=0
do
suml&=0
cls
q$=ucase$(inkey$)
if q$=chr$(27) or q$=" " then exit loop
if q$="H" then
temp!=2*temp!
if temp!>1 then temp!=1 else decr maxpixels&
end if
line(0,0)-(639,479),,b
xt%=1+xtmax!*log(temp!/mintemp!) 'Temperature marker
preset(xt%,479): preset(xt%-1,479)
yt%=1+478*(maxpixels&-minpix&)/(maxpix&-minpix&)
preset(639,yt%): preset(639,yt%-1)
call arraycopy(xm%(),x%()): call arraycopy(ym%(),y%())
if q$<>"B" then
for i%=1 to n%
if rnddmax% and 8*dmin%<5*dmax% then suml&=suml&+2 'Correct
if 1.143*dmin%>dmax% then suml&=suml&+2 'diagonals
'suml&=suml&+dmax%\128 'Penalize long connections
'if dmax% then suml&=suml&+128\dmax% 'and overly short ones too
line(x1%,y1%)-(x2%,y2%)
if q$="B" then
pset(xt%,479): pset(xt%-1,479)
pset(639,yt%): pset(639,yt%-1)
locate 1,1: print file2$;
l%=sqr(dx%*dx%+dy%*dy%)
if l% then 'Put arrows on connections
xo%=(x1%+x2%)/2: yo%=(y1%+y2%)/2
dx%=4*dx%/l%: dy%=4*dy%/l%
line(xo%,yo%)-(xo%-2*dx%+dy%,yo%-2*dy%-dx%)
line(xo%,yo%)-(xo%-2*dx%-dy%,yo%-2*dy%+dx%)
else 'Put loop back to node
circle(x1%+9,y1%),9
end if
end if
next j%,i%
if q$="B" then delay 3: iterate
pix&=pixels&-suml&
if minpix&=0 then minpix&=pix&
if pix&>=maxpixels& then
call arraycopy(x%(),xm%()): call arraycopy(y%(),ym%())
temp!=2*temp! 'Add heat if still settling
if temp!>1 then temp!=1
if pix&>maxpixels& then 'A better solution was found
maxpixels&=pix&
delay 1 'Admire the solution for a second
end if
end if
loop
END SUB
SUB ArrayCopy (x%(), y%())
'Copies the integer array x%() to the array y%(i)
'Only works for arrays with < 16376 elements
'(ArrayLen% must fit in one string segment of 32750 bytes)
i1% = LBOUND(x%)
Elements% = UBOUND(x%) - i1% + 1
SourceSeg& = VARSEG(x%(i1%))
SourceOfs& = VARPTR(x%(i1%))
DestSeg& = VARSEG(y%(i1%))
DestOfs& = VARPTR(y%(i1%))
SourceAbs& = SourceSeg& * 16 + SourceOfs&
DestAbs& = DestSeg& * 16 + DestOfs&
ArrayLen% = Elements% * 2 'Integers have 2 bytes per element
DEF SEG = 0
POKE$ DestAbs&, PEEK$(SourceAbs&, ArrayLen%)
DEF SEG
END SUB
FUNCTION pixels&
'Counts the pixels illuminated on the screen (screen mode 11 only)
LOCAL n1??, n2??
if PbvScrnMode <> 11 then exit function 'Not a supported screen mode
asm push ds
asm push si
asm mov ax, &HA000
asm mov ds, ax
asm xor ax, ax
asm mov bx, ax
asm mov si, ax
asm mov cx, &H4B00
screen_count:
asm push cx
asm mov cx, &H10
asm mov dx, word ptr[si]
asm inc si
asm inc si
byte_count:
asm shl dx, 1
asm jnc around_inc
asm add ax, 1
asm jnc around_inc
asm inc bx
around_inc:
asm loop byte_count
asm pop cx
asm loop screen_count
asm mov n1??, bx
asm mov n2??, ax
asm pop si
asm pop ds
pixels& = 65536 * n1?? + n2??
END FUNCTION