'One level up
'******************************************************************************
'Subject: Auto-associative memory, John Hopfield's 1982 discrete
'         recurrent network: a toy engine for pattern recall.
'Author : Sjoerd.J.Schaper - vspickelen
'Date   : 12-14-2005
'Code   : all QBasic's, FreeBasic extendable
'Keys   : [Esc] quit program
'******************************************************************************
'This program is copyright (c) 2005 by the author. It is made available as is,
'and no warranty - about the program, its performance, or its conformity to any
'specification - is given or implied. It may be used, modified, and distributed
'freely, as long as the original author is credited as part of the final work.
'******************************************************************************
REM $STATIC
DEFINT A-Z

DECLARE SUB mapsin (sw)
'read map file
DECLARE SUB weights ()
'compute link weights
DECLARE SUB shuffle (nr)
'random update sequence
DECLARE FUNCTION propag (t)
'propagate signals
DECLARE SUB paints (t, a, nr)
'paint network cell t
DECLARE SUB prints ()
'print cell states

CONST MODE = 12, dW = 640, dH = 480
' set screenmode, width, height
'CONST MODE = 19, dW = 800, dH = 600 ' FreeBasic only

CONST IX = 13, IY = 13              ' bit box
CONST NI = IX * IY                  ' number of cells
CONST NO = 10                       ' number of bitmaps
CONST DEL = 20                      ' graphics delay

DIM SHARED bmp(NI * NO - 1)         ' binary bitmaps
DIM SHARED ic(NI - 1)               ' cell indices
DIM SHARED act(NI - 1)              ' bipolar activations
DIM SHARED wei(NI - 1, NI - 1)      ' weight matrix

mapsin 1 '                            training file
weights '                             imprint in memory

mapsin 2 '                            testing file
OPEN ".\result.txt" FOR OUTPUT AS 1

FOR n = 0 TO NO - 1

   t = n * NI
   FOR r = 0 TO NI - 1
      act(r) = bmp(t + r) '           copy single bitmap
      paints r, act(r), 0
   NEXT r
   LOCATE 2, 2: PRINT "map "; n + 1; " ";

   m = 0
   DO
      shuffle 19

      FOR r = 0 TO NI - 1 '           asynchronous updates
         m = m + 1
         
         IF propag(r) THEN m0 = m
         LOCATE 3, 2: PRINT "iter"; m; " ";
         IF INKEY$ <> "" THEN EXIT DO
      NEXT r
   LOOP UNTIL m - m0 > NI

   prints '                           output test state
NEXT n

SLEEP
CLOSE
SYSTEM

SUB mapsin (sw)
   l$ = ".\patterns.lif"
   IF sw > 1 THEN l$ = ".\badpatts.lif"
   OPEN l$ FOR INPUT AS 1

   FOR r = 0 TO NO * IY - 1
      DO
         IF NOT EOF(1) THEN LINE INPUT #1, l$
      LOOP WHILE l$ = ""
      FOR s = 0 TO IX - 1 '           scan line
         t = r * IX + s: bmp(t) = -1 '    low
         IF MID$(l$, s + 1, 1) = "*" THEN
            bmp(t) = 1 '                  high
         END IF
      NEXT s
   NEXT r
   CLOSE 1
END SUB

SUB paints (t, a, nr)
STATIC fl
IF NOT fl THEN
   SCREEN MODE
   PALETTE 0, &H180000
   PALETTE 1, &H0
   PALETTE 2, &H303030: COLOR 2
   r = 1 + dW * .25 * .618
   s = r + dW * .75 - 1: fl = -1
   VIEW (r, 1)-(s, dH - 2), 0, 2

   dx! = IX - .5: dy! = IY - .5
   WINDOW SCREEN (-.5, -.5)-(dx!, dy!)
   n = IX: IF IY > IX THEN n = IY '   max(IX, IY)
   FOR r = 0 TO n
      dt! = r - .5
      LINE (dt!, -.5)-(dt!, dy!), 1 ' draw grid
      LINE (-.5, dt!)-(dx!, dt!), 1
   NEXT r
END IF

   r = t \ IX
   s = t - r * IX
   PAINT (s, r), a + 1, 1 '           update cell
   SLEEP nr '                         wait...
END SUB

SUB prints
   g$ = STRING$(NI + IY * 2, " ")
   CrLf$ = CHR$(13) + CHR$(10)

   t = 1
   FOR r = 1 TO NI '                  extract pattern
      IF act(r - 1) = 1 THEN
         MID$(g$, t, 1) = "*"
      END IF
      t = t + 1
      IF r MOD IX = 0 THEN
         MID$(g$, t, 2) = CrLf$
         t = t + 2
      END IF
   NEXT r
   PRINT #1, g$
END SUB

FUNCTION propag (t)
    r = ic(t): z = 0
    FOR s = 0 TO NI - 1
       z = z + wei(r, s) * act(s)
    NEXT s '                          compute inner product

    sw = 0
    IF z <> 0 THEN '                  zero threshold
       n = 1: IF z < 0 THEN n = -1 '  pass through hard limiter
       IF act(r) <> n THEN
          act(r) = n: sw = -1
          paints r, n, DEL
       END IF
    END IF

propag = sw
END FUNCTION

SUB shuffle (nr)
   FOR r = 0 TO NI - 1
      ic(r) = r
   NEXT r

   FOR t = 1 TO nr '                  switch indices
      r = INT(RND * NI)
      s = INT(RND * NI)
      SWAP ic(r), ic(s)
   NEXT t
END SUB

SUB weights
   FOR n = 0 TO NO - 1 '              sum outer products
      t = n * NI '                    map offset
      FOR r = 0 TO NI - 1
         z = bmp(t + r)
         FOR s = 0 TO NI - 1
            wei(r, s) = wei(r, s) + z * bmp(t + s)
         NEXT s
      NEXT r
   NEXT n

   FOR r = 0 TO NI - 1 '              zero diagonal
      wei(r, r) = 0
   NEXT r
END SUB
'