[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)