[From Bill Powers (2002.11.09.1723 MST)]

Bill Williams UMKC 9 November 2002 12:30 AM CST--

> Bill's version of the

model generates a Giffen effect with two control loops and a subtle trick.

I think it is clever, but I think it makes it more difficult to explain

what is happening-- witness Rick's misundertanding.

My model uses three loops: a budget loop, a loop for calorie control, and a

"prestige" loop that establishes a bias in favor of meat over bread. The

bias is necessary, otherwise all the calories can come from bread at any price.

Also, my model is not hierarchical. The three loops all operate at once, in

parallel. Actually, I thought "my" model was the same as "your" model.

I attach the source and the .exe program -- it's not very long. I think the

instructions on the screen may be enough to figure out how to work it -- if

not I'll supply a writeup.

best

Bill Williams

program giffen;

uses dos,crt,graph,grUtils,textunit;

const labels: array[0..4] of string[10] =

('Refer','Gain','Slow','BredVal','MeatVal');

Esc = $1b;

type variablekind = (calories,cost,prestige);

arraytype = array[calories..prestige] of real;

const categories: array[0..2] of string[8] =

('Calories','Cost ','Prestige');

var RefSignal,Perception,error,Output,gain,loopgain,slowing,

meatfactor,breadfactor,meataction,breadaction: arraytype;

meat,bread: real;

i,j: integer;

outdevname: string[30];

ch: char;

cmd: byte;

ctsystem: variablekind;

changed: boolean;

numstr: string[30];

frameheight: integer;

color1,color2,color3: integer;

vsize,hsize,vcenter,hcenter: integer;

procedure initscreen;

begin

initgraphics;

hsize := getmaxx; vsize := getmaxy;

end;

procedure initialize;

begin

RefSignal[calories] := 5000.0;

RefSignal[cost] := 5000.0;

RefSignal[prestige] := 5000.0;

meatfactor[calories] := 100.0; { input effects of meat }

meatfactor[cost] := 200.0;

meatfactor[prestige] := 100.0;

breadfactor[calories] := 66.0; { input effects of bread }

breadfactor[cost] := 30.0;

breadfactor[prestige] := -10.0;

meataction[calories] := 1.0; { all 3 ctsystems affect meat purchases }

meataction[cost] := 1.0; { when budget goes over, meat reduced }

meataction[prestige] := 1.0;

breadaction[calories] := 1.0;

breadaction[cost] := 0.0; { cost considerations don't affect bread }

breadaction[prestige] := -1.0; { bread affects prestige negatively }

gain[calories] := 9.0; { higher gain means more importance }

gain[cost] := 5.0;

gain[prestige] := 0.1; { a minor factor with a big effect ! }

slowing[calories] := 0.7; { adjusted for stability. Relative to }

slowing[cost] := 0.8; { optimum slowing factor of 1.00 }

slowing[prestige] := 0.4

end;

procedure changevar;

const j: variablekind = calories;

k: integer = 0;

var datamatrix:

array[0..1] of array[calories..prestige] of array[0..4] of real;

error,xx,yy,x,y,i,n,l: integer;

realstr: string[15];

realvar: real;

m: variablekind;

procedure writeval(j: variablekind; k: integer);

begin

case k of

0: write(RefSignal[j]:7:0);

1: write(gain[j]:7:3);

2: write(slowing[j]:7:3);

3: write(breadfactor[j]:7:3);

4: write(meatfactor[j]:7:3)

end;

end;

begin

restorecrtmode;

textcolor(white);

clrscr;

gotoxy(1,25); write('Arrows = move (enter values) End = plot');

gotoxy(1,1);

for m := calories to prestige do

begin

y := 10 * ord(m) + 2;

gotoxy(1,y);

write(categories[ord(m)]:8);

for n := 0 to 4 do

begin

gotoxy(10*n + 10,y+1); write(labels[n]:7);

gotoxy(10*n + 10,y+2);

writeval(m,n);

end;

end;

repeat

y := 10 * ord(j) + 4;

x := 10 * k + 10;

for m := calories to prestige do

begin

loopgain[m] := (breadfactor[m] + meatfactor[m])*gain[m];

gotoxy(30, 10 * ord(m) + 1); write('Loop gain = ', loopgain[m]:7:2);

end;

textattr := $70;

gotoxy(x,y); writeval(j,k);

case k of

0: realvar := RefSignal[j];

1: realvar := gain[j];

2: realvar := slowing[j];

3: realvar := breadfactor[j];

4: realvar := meatfactor[j];

end;

cmd := getrealstr(realvar,7);

case k of

0: RefSignal[j] := realvar;

1: gain[j] := realvar;

2: slowing[j] := realvar;

3: breadfactor[j] := realvar;

4: meatfactor[j] := realvar;

end;

textattr := $0f;

gotoxy(x,y); blanks(7); writeval(j,k);

case cmd of

Left: if k > 0 then k := k - 1 else k := 4;

Right: if k < 4 then inc(k) else k := 0;

Up: if j > calories then dec(j) else j := prestige;

Down: if j < prestige then inc(j) else j := calories;

end;

until cmd = EndKey;

setgraphmode(graphmode);

end;

procedure plotit(i: integer);

var loc: word;

z2,z3: integer;

begin

settextjustify(CenterText,BottomText);

loc := frameheight;

z2 := hsize div 3; z3 := z2 + z2 + i;

z2 := z2 + i;

putpixel(i,loc - round(RefSignal[calories]/100.0),color1);

putpixel(i,loc - round(Perception[calories]/100.0),color2);

putpixel(z2,loc - round(RefSignal[cost]/100.0),color1);

putpixel(z2,loc - round(Perception[cost]/100.0),color2);

putpixel(z3,loc - round(RefSignal[prestige]/100.0),color1);

putpixel(z3,loc - round(Perception[prestige]/100.0),color2);

putpixel(z2,vsize - 1,color3);

putpixel(z2,vsize - 2 - round(meat/2.0),color1);

putpixel(z2,vsize - 2 - round(bread/2.0),color2);

end;

begin

textattr := $0f;

clrscr; initialize;

textattr := $0f;

initscreen;

color3 := getmaxcolor;

color2 := color3 - 1;

if color2 < 1 then color2 := 1;

color1 := color3 - 2;

if color1 < 1 then color1 := 1;

restorecrtmode;

repeat

changevar;

setgraphmode(graphmode);

hsize := getmaxx + 1;

vsize := getmaxy + 1;

hcenter := hsize div 2;

vcenter := vsize div 2;

frameheight := (3 * vsize div 4);

settextjustify(CenterText,BottomText);

outtextxy(hsize div 6,9,'CALORIES');

str(RefSignal[calories]:5:0,numstr);

numstr := 'REF = ' + numstr;

outtextxy(hsize div 6,18,numstr);

outtextxy(hcenter,9,'BUDGET');

str(RefSignal[cost]:3:0,numstr);

numstr := 'REF = $' + numstr;

outtextxy(hcenter,18,numstr);

outtextxy((5 * hsize) div 6,9,'PRESTIGE');

str(RefSignal[prestige]:5:0,numstr);

numstr := 'REF = ' + numstr;

outtextxy((5 * hsize) div 6,18,numstr);

setcolor(color1);

str(meatfactor[cost]/100.0:3:2,numstr);

settextjustify(LeftText,CenterText);

numstr := ' MEAT: $' + numstr;

outtextxy(3,vsize - (2 * (vsize - frameheight)) div 3,numstr);

setcolor(color2);

str(breadfactor[cost]/100.0:3:2,numstr);

numstr := 'BREAD: $' + numstr;

outtextxy(3,vsize - (vsize - frameheight) div 3,numstr);

meat := 0.0; bread := 0.0;

for ctsystem := calories to prestige do

begin

Output[ctsystem] := 0;

Perception[ctsystem] := 0;

end;

for j := 0 to frameheight do

begin

putpixel(hsize div 3,j,color3);

putpixel((hsize + hsize) div 3,j,color3);

end;

for j := 0 to vsize - 1 do

begin

putpixel(0,j,color3);

putpixel(hsize - 1,j,color3)

end;

for j := 0 to hsize - 1 do

begin

putpixel(j,frameheight,color3);

putpixel(j,0,color3);

putpixel(j,vsize - 1,color3);

end;

{ calculate effects of each control ctsystem on meat and bread purchases }

for i := 1 to 3 * hsize - 3 do

begin

for ctsystem := calories to prestige do {see declaration, enumerated var}

begin

{ Value computed from meat and bread factors (calories, cost,

or prestige value) and amount obtained. This is the perceptual signal

for calories, cost, and prestige }

Perception[ctsystem] := meat * meatfactor[ctsystem]

+ bread * breadfactor[ctsystem];

{ error in calories, cost, and prestige. "RefSignal" is reference

signal.}

error[ctsystem] := RefSignal[ctsystem] - Perception[ctsystem];

{If the Perception expenditures are less than the budgeted expenditures,

there is no error. If calories exceed RefSignals there is no error.}

if ctsystem = cost then if error[ctsystem] > 0 then error[ctsystem] := 0;

if ctsystem = calories then if error[ctsystem] < 0 then error[ctsystem] := 0;

{

"Output" is the tendency to buy bread and meat. There are three control

ctsystems. See the next comment below. The equation below is equivalent to

"Output[ctsystem] := error[ctsystem] * gain[ctsystem]",

but contains a slowing factor which complicates it. "Loopgain[ctsystem]"

is computed when the constants are entered.

}

Output[ctsystem] := Output[ctsystem]

+ slowing[ctsystem]/(1.0 + loopgain[ctsystem]) *

(error[ctsystem] * gain[ctsystem] - Output[ctsystem]);

end;

meat := 0; bread := 0;

for ctsystem := calories to prestige do

begin

{ The Output of each control ctsystem, reflecting the error, loop gain,

and slowing factor, is multiplied by the "meataction" or "breadaction"

constants, which are meant to be 1, 0, or -1. If the action is 0,

there is no connection. The +1 or -1 is chosen for negative feedback.

Each ctsystem's Output represents either an amount purchased or a

negative amount, cancelling other amounts purchased. The net amount

purchased is the sum of the Outputs times their gain constants. Note

that "ctsystem" is an enumerated variable with values "calories", "cost"

and "prestige". These have the ordinal values 0,1, and 2.}

{ These are the actual purchases }

meat := meat + Output[ctsystem] * meataction[ctsystem];

bread := bread + Output[ctsystem] * breadaction[ctsystem];

end;

{ Real world: don't let meat or bread go negative }

if meat < 0.0 then meat := 0.0;

if bread < 0.0 then bread := 0.0;

if i mod 9 = 0 then plotit(i div 9);

end;

settextjustify(CenterText,BottomText);

str(100.0 * Perception[calories]/RefSignal[calories]:3:0,numstr);

numstr := numstr + '%';

outtextxy(hsize div 6,frameheight - 2,numstr);

str(100.0 * Perception[cost]/RefSignal[cost]:3:0,numstr);

numstr := numstr + '%';

outtextxy(hcenter,frameheight - 2,numstr);

if RefSignal[prestige] > 0.0001 then

begin

str(100.0 * Perception[prestige]/RefSignal[prestige]:3:0,numstr);

numstr := numstr + '%';

outtextxy((5 * hsize) div 6,frameheight - 2,numstr);

end;

settextjustify(CenterText,BottomText);

if (meat + bread) <> 0 then

begin

str(100.0 * bread/(meat + bread):3:0,numstr);

numstr := numstr + '% BREAD';

outtextxy(hcenter - 1,frameheight + 9,numstr);

end;

setcolor(color3);

settextjustify(RightText,BottomText);

outtextxy(hsize - 1,vsize - 1, 'Quit= Esc');

outtextxy(hsize - 1,vsize - 21,'More=Space');

cmd := getcod;

until cmd = Esc;

closegraph;

end.

GIFFEN2.EXE (47.8 KB)