ECOLI4, Captain

[From Bruce "the animist" Abbott (941104.1500 EST)]

The aliens have completed their work on ECOLI4, and have asked me to transmit
it to Enterprise. As they have explained to me, they found that they were
forced to use some rather complex arrangements in order to demonstrate
discriminated reinforcement/punishment of tumbling behavior. Although they
admit that this is very un-e.-coli-like, they wanted me to point out to the
Enterprise crew that while real e. coli do not learn, more advanced creatures
such as the Funge-wergels of Splorjab occasionally do. ECOLI4 shows the
reinforcement principle at work in a somewhat more complex e. coli that had to
learn what to do in order to approach its nutrients.

As Spock will no doubt quickly discover, ECOLI4 models the three-term
contingency suggested by Sam Saunders, with whom they have been in recent
communication via the csg-l subspace carrier. Tumbling occurs in the presence
of increasing nutrient levels (Tumbling given S+) or decreasing nutrient
levels (Tumbling given S-). On the next tick after a tumble, e. coli
skinnerius determines whether the tumble has resulted in an increasing
nutrient level (Reinforcement) or decreasing nutrient level (Punishment).
These consequences of tumbling then act to alter the probability of tumbling
in the presence of the stimulus that was present during the tumble (S+ or S-).
If tumbling led to increasing nutrients, its probability is decreased; if to
decreasing nutrients, it is increased. [Note: the aliens recognize that this
is opposite to the usual effects of reinforcement and punishment but feel that
selective pressures during evolution would have produced this unique
arrangement, given e. coli's highly restricted response capabilities. The
aliens are very good at verbal rationalizations, as you have probably already
noticed.]

Bruce

{***********************************************************************
* E. COLI "REINFORCEMENT" SIMULATION 4 *
* *
* Programmer: Dr. Bruce B. Abbott *
* Psychological Sciences *
* Indiana U. - Purdue U. *
* Fort Wayne, IN 46805-1499 *
* (219) 481-6399 *
* Language: Borland/Turbo Pascal 7.0 *
* Created: 11/03/94 *
* *
* This program implements a "selection-by-consequences" discriminated *
* operant simulation of the "tumble-and-swim" behavior of e. coli. *
* *
***********************************************************************}

program Ecoli4;

uses
  CRT, Graph, GrUtils;

const
  TWOPI = PI * 2;
  ENDSESSION = 10000;
var
  MaxX, MaxY: integer;
  NutrX, NutrY, X, Y: integer;
  NutMag, NutCon, dNut, NutSave: real;
  EcoliX, EcoliY,
  Speed, Angle, DecayRate, LearnRate,
  pTumbleGivenSplus, pTumbleGivenSminus,
  pMax, pMin: real;
  JustTumbled: boolean;
  Ch: char;
  Clock: longint;

procedure InitScreen;
begin
  ClrScr;
  InitGraphics;
  MaxX := GetMaxX; MaxY := GetMaxY;
  Rectangle(0, 0, MaxX, MaxY);
  OutTextXY(MaxX div 2 - 220, Y+5,
    'E. COLI SIMULATION # 4: DISCRIMINATED REINFORCEMENT MODEL');
  OutTextXY(MaxX - 200, 50, ' dNutrient');
  OutTextXY(MaxX - 200, 60, 'p(Tumble|S+)');
  OutTextXy(MaxX - 200, 70, 'p(Tumble|S-)');
  OutTextXY(20, MaxY-50, 'Press ESC to Quit...');
end;

procedure ShowReal(x,y: integer; v: real);
var s: string;
begin
str(v:8:4, s);
setfillstyle(0,0);
bar (x,y,x+textwidth(s),y+textheight(s));
outtextxy(x,y,s);
end;

procedure Tumble(var Angle: real);
begin
  Angle := TwoPi * Random;
end;

procedure InitSim;
begin
  Randomize;
  NutrX := MaxX div 2;
  NutrY := MaxY div 2;
  EcoliX := 50.0;
  EcoliY := 50.0;
  X := Round(EcoliX);
  Y := Round(EcoliY);
  Rectangle(NutrX-2, NutrY-2, NutrX+2, NutrY+2);
  Speed := 1.0;
  DecayRate := 0.25;
  LearnRate := 0.005;
  JustTumbled := false;
  NutSave := 0;
  pMax := 0.500; { Maximum tumble rate }
  pMin := 0.05; { Minimum tumble rate }
  pTumbleGivenSplus := 0.050; { initial tumble rates }
  pTumbleGivenSminus := 0.050;
  NutMag := 100.0; { max concentration }
  repeat Tumble(Angle) until (Angle < PI/2);
  Clock := 0;
end;

function NutConcen(X, Y: real): real;
{ Nutient concentration at point X, Y: environment function }
var
  Dist: real;
begin
  Dist := Sqrt(Sqr(X - NutrX) + Sqr(Y - NutrY));
  NutConcen := NutMag / (1 + 0.001*(Sqr(Dist)));
end;

procedure StepEColi;
var
  NewNut: real;

  procedure ReinforceOrPunish;
  begin
    If dNut > 0 then { nutrients rising: reinforcement }
      begin
        If NutSave > 0 then { S+ present when last tumbled }
          begin
            if pTumbleGivenSplus > pMin then
              pTumbleGivenSplus := pTumbleGivenSplus - LearnRate;
          end
        else { S- present when last tumbled }
          begin
            if pTumbleGivenSminus > pMin then
              pTumbleGivenSminus := pTumbleGivenSminus - LearnRate;
          end
      end
    else { nutrients not rising: punishment }
      begin
        If NutSave > 0 then { S+ present when last tumbled }
          begin
            if pTumbleGivenSplus < pMax then
              pTumbleGivenSplus := pTumbleGivenSplus + LearnRate;
          end
        else { S- present when last tumbled }
          begin
            if pTumbleGivenSminus < pMax then
              pTumbleGivenSminus := pTumbleGivenSminus + LearnRate;
          end;
      end;
  end;
  procedure DoTumble;
  begin
    Tumble(Angle);
    JustTumbled := true;
    NutSave := dNut;
  end;
begin
  EcoliX := EcoliX + Speed * cos(Angle);
  EcoliY := EcoliY + Speed * sin(Angle);
  X := Round(EcoliX);
  Y := Round(EcoliY);
  PutPixel(X, Y, white);
  NewNut := NutConcen(EcoliX, EcoliY);
  dNut := DecayRate * dNut + (NewNut - NutCon);

  if JustTumbled then ReinforceOrPunish;

  if dNut > 0 then { Nurients increasing }
    begin
      if (Random < pTumbleGivenSplus) then DoTumble
      else JustTumbled := false;
    end
  else { Nutrients stable or decreasing }
    begin
      if (Random < pTumbleGivenSminus) then DoTumble
      else JustTumbled := false;
    end;
  NutCon := NewNut;

  showreal(maxx - 100, 50, dNut);
  showreal(maxx - 100, 60, pTumbleGivenSplus);
  showreal(maxx - 100, 70, pTumbleGivenSminus);
end;

var i: integer;
    z: real;
begin
  InitScreen;
  InitSim;
  repeat
    inc(Clock);
    StepEcoli;
    outtextxy(1,1, '');
    if Keypressed then Ch := ReadKey;
  until (Ch = #27) or (Clock >= ENDSESSION);
  if Ch <> #27 then Ch := ReadKey;
  RestoreCRTMode;
  CloseGraph;
end.