diff -cr ASM-20011125/ASMBatchSwarm.h ASM-2.2/ASMBatchSwarm.h *** ASM-20011125/ASMBatchSwarm.h Wed Oct 24 18:26:08 2001 --- ASM-2.2/ASMBatchSwarm.h Wed Oct 31 13:58:28 2001 *************** *** 6,21 **** @interface ASMBatchSwarm: Swarm { ! int loggingFrequency; ! int experimentDuration; ! id displayActions; ! id displaySchedule; ! id stopSchedule; ! ASMModelSwarm * asmModelSwarm; ! id output; } + createBegin: aZone; --- 6,21 ---- @interface ASMBatchSwarm: Swarm { ! int loggingFrequency; /*"how often to write data "*/ ! int experimentDuration;/*"how long should a run last"*/ ! id displayActions;/*"Set of actions that update output"*/ ! id displaySchedule;/*"Schedule for periodic actions"*/ ! id stopSchedule; /*"Schedule which checks to see if the simulation has completed its requisite number of timesteps"*/ ! ASMModelSwarm * asmModelSwarm;/*"Instance of ASMModelSwarm, where agents and the world are created and scheduled"*/ ! id output; /*"Reference to instance of Output class, the place where data file output is controlled"*/ } + createBegin: aZone; diff -cr ASM-20011125/ASMBatchSwarm.m ASM-2.2/ASMBatchSwarm.m *** ASM-20011125/ASMBatchSwarm.m Wed Oct 24 18:26:08 2001 --- ASM-2.2/ASMBatchSwarm.m Tue Dec 4 13:28:37 2001 *************** *** 2,7 **** --- 2,12 ---- #import "Parameters.h" @implementation ASMBatchSwarm + /*"If the model is started with ./asm -b, then this ASMBatchSwarm acts + as the top level Swarm. The benefit here is that the model runs + faster because it is not driving a graphical interface. It also + turns on data-writing."*/ + +createBegin: (id)aZone { *************** *** 16,33 **** return obj; } ! -buildObjects { id modelZone; BFParams * bfParams = [(id)arguments getBFParams]; ASMModelParams * asmModelParams = [(id)arguments getModelParams]; [super buildObjects]; modelZone = [Zone create: [self getZone]]; asmModelSwarm = [ASMModelSwarm create: modelZone]; ! [asmModelSwarm setParamsModel: asmModelParams BF: bfParams]; //ObjectLoader: is deprecated --- 21,42 ---- return obj; } ! /*"Create a model swarm, have the model swarm build its objects, and ! then get the output object from the model. Later the output object is ! instructed to write results"*/ -buildObjects { id modelZone; BFParams * bfParams = [(id)arguments getBFParams]; ASMModelParams * asmModelParams = [(id)arguments getModelParams]; + output = [[Output createBegin: self] createEnd]; [super buildObjects]; modelZone = [Zone create: [self getZone]]; asmModelSwarm = [ASMModelSwarm create: modelZone]; ! [asmModelSwarm setOutputObject: output]; ! [asmModelSwarm setParamsModel: asmModelParams BF: bfParams]; //ObjectLoader: is deprecated *************** *** 44,50 **** return self; } ! -buildActions { [super buildActions]; --- 53,59 ---- return self; } ! /*"Create schedules. Assures that the output object writes the data when needed and checks to see if the required number of time steps has been completed"*/ -buildActions { [super buildActions]; *************** *** 68,74 **** return self; } ! -activateIn: (id)swarmContext { [super activateIn: swarmContext]; --- 77,83 ---- return self; } ! /*"activateIn: is required to preserve the hierarchy of schedules across many levels"*/ -activateIn: (id)swarmContext { [super activateIn: swarmContext]; *************** *** 82,88 **** } ! - expostParamWrite { [[asmModelSwarm getOutput] writeParams: [(id) arguments getModelParams] BFAgent: [(id) arguments getBFParams] Time: [asmModelSwarm getModelTime]]; --- 91,97 ---- } ! /*"Tell the objects that are keeping records on parameter values to write them to files at the end of the simulation."*/ - expostParamWrite { [[asmModelSwarm getOutput] writeParams: [(id) arguments getModelParams] BFAgent: [(id) arguments getBFParams] Time: [asmModelSwarm getModelTime]]; *************** *** 90,95 **** --- 99,107 ---- } + /*"Once schedules are created and activatedIn to the right part of the + hierarchy, then go makes processing start with actions at time 0, + then 1, then..."*/ -go { printf("\nYou typed 'asm -batchmode'. The simulation is running without graphics.\n\n"); *************** *** 102,108 **** return [[self getActivity] getStatus]; } ! -stopRunning { [getTopLevelActivity() terminate]; --- 114,120 ---- return [[self getActivity] getStatus]; } ! /*"tell the top level swarm to terminate the simulation"*/ -stopRunning { [getTopLevelActivity() terminate]; *************** *** 110,115 **** --- 122,129 ---- } + /*" The drop method lets objects know the simulation is coming to an + end, so if they are waiting to write some data, they should do it"*/ -(void) drop { [asmModelSwarm drop]; diff -cr ASM-20011125/ASMModelParams.h ASM-2.2/ASMModelParams.h *** ASM-20011125/ASMModelParams.h Sun Jul 9 16:36:44 2000 --- ASM-2.2/ASMModelParams.h Fri Oct 26 11:46:52 2001 *************** *** 3,9 **** @interface ASMModelParams: SwarmObject { @public ! int numBFagents; float initholding; double initialcash; double minholding; --- 3,9 ---- @interface ASMModelParams: SwarmObject { @public ! int numBFagents; /*" number of BFagents "*/ float initholding; double initialcash; double minholding; *************** *** 38,44 **** double initvar; double maxdev; int setOutputForData; - }; @end --- 38,43 ---- diff -cr ASM-20011125/ASMModelParams.m ASM-2.2/ASMModelParams.m *** ASM-20011125/ASMModelParams.m Wed Feb 7 15:30:52 2001 --- ASM-2.2/ASMModelParams.m Fri Oct 26 11:46:52 2001 *************** *** 3,9 **** @implementation ASMModelParams ! +createBegin: (id) aZone { --- 3,22 ---- @implementation ASMModelParams ! /*"The ASMModelParams class is a "holding class" for the parameters ! that are associated with the ASMModelSwarm class and it also ! controls the GUI probes that users can use to change variables at ! runtime. These values are set here in order to keep the code clean ! and neat! Several parts of the simulation need to have access to ! the information held by ASMModelParams, not just ASMModelParams, but ! also any classes that want information on system parameters. ! ! A big reason for keeping these values in a separate class is that ! they can be used by both batch and graphical runs of the model. ! ! There are values saved for these parameters in the asm.scm file, and ! the Parameters class, which orchestrates all this parameter magic, ! does the work of creating one of these ASMModelParams objects."*/ +createBegin: (id) aZone { *************** *** 16,27 **** [probeMap setProbedClass: [self class]]; probeMap = [probeMap createEnd]; ! [probeMap addProbe: [probeLibrary getProbeForVariable: "numBFagents" inClass: [self class]]]; [probeMap addProbe: [probeLibrary getProbeForVariable: "initholding" inClass: [self class]]]; [probeMap addProbe: [probeLibrary getProbeForVariable: "initialcash" ! inClass: [self class]]]; [probeMap addProbe: [probeLibrary getProbeForVariable: "minholding" inClass: [self class]]]; [probeMap addProbe: [probeLibrary getProbeForVariable: "mincash" --- 29,40 ---- [probeMap setProbedClass: [self class]]; probeMap = [probeMap createEnd]; ! [probeMap addProbe: [probeLibrary getProbeForVariable: "numBFagents" inClass: [self class]]]; [probeMap addProbe: [probeLibrary getProbeForVariable: "initholding" inClass: [self class]]]; [probeMap addProbe: [probeLibrary getProbeForVariable: "initialcash" ! inClass: [self class]]]; [probeMap addProbe: [probeLibrary getProbeForVariable: "minholding" inClass: [self class]]]; [probeMap addProbe: [probeLibrary getProbeForVariable: "mincash" *************** *** 81,87 **** [probeLibrary setProbeMap: probeMap For: [self class]]; ! return obj; } --- 94,100 ---- [probeLibrary setProbeMap: probeMap For: [self class]]; ! return obj; } diff -cr ASM-20011125/ASMModelSwarm.h ASM-2.2/ASMModelSwarm.h *** ASM-20011125/ASMModelSwarm.h Wed Oct 24 18:26:08 2001 --- ASM-2.2/ASMModelSwarm.h Fri Nov 2 16:57:18 2001 *************** *** 9,103 **** @interface ASMModelSwarm: Swarm { ! // //Agent parameters ! // int numBFagents; ! // float initholding; ! // double initialcash; ! // double minholding; ! // double mincash; ! // double intrate; ! ! // //Dividend parameters ! // double baseline; //Also used by World. ! // double mindividend; ! // double maxdividend; ! // double amplitude; ! // int period; ! ! // //Specialist parameters ! // double maxprice; ! // double minprice; ! // double taup; ! // BOOL exponentialMAs; //Also used by World. ! // int sptype; ! // int maxiterations; ! // double minexcess; ! // double eta; ! // double etamax; ! // double etamin; ! // double rea; ! // double reb; ! ! // int randomSeed; ! ! //Agent parameters overridden by the BFagent. ! //These might be used for other agents that a user implements. ! // double tauv; ! // double lambda; ! // double maxbid; ! // double initvar; ! // double maxdev; ! ! int modelTime; ! id warmupActions; ! id periodActions; ! id warmupSchedule; ! id periodSchedule; ! id startupSchedule; ! id initPeriodSchedule; ! ! id agentList; ! id specialist; ! id dividendProcess; ! id world; ! id output; ! // int setOutputForData; ! ! BFParams * bfParams; //pj: a parameter object ! ASMModelParams * asmModelParams; //pj: a parameter object ! } - - //+createBegin: (id)aZone; - -createEnd; ! - setParamsModel: (ASMModelParams *) modelParams BF: (BFParams *) bfp ; //pj: new param receiver ! -getAgentList; ! -(int)getNumBFagents; ! -(double)getInitialCash; ! -(World *)getWorld; ! -(Specialist *)getSpecialist; ! -(Output *)getOutput; ! -setBatchRandomSeed: (int)newSeed; ! ! -buildObjects; ! // pj: -initOutputForDataWrite; ! // pj: -initOutputForParamWrite; - writeParams; - buildActions; - activateIn: (id)swarmContext; ! void warmUp(id warmupSchedule); ! void initPeriod(id initPeriodSchedule); ! ! -warmupStepDividend; ! -warmupStepPrice; ! -periodStepDividend; ! //-prepareBFagentForTrading; ! -periodStepPrice; -(long int) getModelTime; --- 9,54 ---- @interface ASMModelSwarm: Swarm { ! int modelTime; /*"An integer used to represent the current timestep"*/ ! ! id periodActions; /*" An ActionGroup that collects things that are supposed to happen in a particular sequential order during each timestep "*/ ! id periodSchedule; /*" Schedule on which we add period (repeating) actions, most importantly, the action group periodActions"*/ ! id startupSchedule; ! ! id agentList; /*"A Swarm collection of agents "*/ ! Specialist * specialist; /*"Specialist who clears the market "*/ ! Dividend * dividendProcess; /*"Dividend process that generates dividends "*/ ! World * world; /*"A World object, a price historian, really "*/ ! Output * output; /*"An Output object "*/ ! ! BFParams * bfParams; /*" A (BFParams) parameter object holding BFagent parameters"*/ ! ASMModelParams * asmModelParams; /*" A (ASMModelParms) parameter object holding parameters of Models"*/ ! ! } -createEnd; ! - setParamsModel: (ASMModelParams *) modelParams BF: (BFParams *) bfp ; ! - setOutputObject: (Output *) obj; ! //pj: new param receiver ! - getAgentList; ! - (int)getNumBFagents; ! - (double)getInitialCash; ! - (World *)getWorld; ! - (Specialist *)getSpecialist; ! - (Output *)getOutput; ! - setBatchRandomSeed: (int)newSeed; ! ! - buildObjects; - writeParams; - buildActions; - activateIn: (id)swarmContext; + - doWarmupStep; ! - periodStepDividend; ! - periodStepPrice; -(long int) getModelTime; *************** *** 105,107 **** --- 56,66 ---- -(void) drop; @end + + + + + + + + diff -cr ASM-20011125/ASMModelSwarm.m ASM-2.2/ASMModelSwarm.m *** ASM-20011125/ASMModelSwarm.m Wed Oct 24 18:26:08 2001 --- ASM-2.2/ASMModelSwarm.m Sun Nov 25 20:29:58 2001 *************** *** 9,24 **** @implementation ASMModelSwarm ! - createEnd { modelTime=0; ! //need to create output so it exists from beginning ! output = [[Output createBegin: [self getZone]] createEnd]; return [super createEnd]; } - setParamsModel: (ASMModelParams *) modelParams BF: (BFParams *) bfp { bfParams = bfp; --- 9,78 ---- @implementation ASMModelSwarm ! /*"The ASMModelSwarm is where the substantive work of the simulation ! is orchestrated. The ASMModelSwarm object is told where to get its ! parameters, and then it buildsObjects (agents, markets, etc), it ! builds up a phony history of the market, and then it schedules the ! market opening and gives the agents a chance to buy and sell. ! ! This model presents an interesting scheduling challenge. We want to ! generate 501 periods of history that agents can refer to when they ! make decisions. The warmUp schedule is a repeating schedule, and we ! want its actions done 501 times, and when that is finished, we want ! the periodSchedule to begin at time 0, the starting time of actual ! agent involvement. When I looked at the original, I shuddered at ! the complexity of it. I thought to myself, there must be a simpler ! way to do this [grin :)], and it turns out there is. Now, in case ! you are comparing the new code against the old code, understand that ! the old ASM-2.0 way was like this. First, the warmUp schedule is ! created. Then a second nonrepeating schedule is created, called ! "startupSchedule." At time 0 in the model, that startupSchedule ! controls the first action, and the action it executes is a method ! that causes the warmUp schedule to run 501 steps of prehistory. I ! don't know why they had 501 steps, but they did. That's the warmUp ! method. The warmUp method gets that done by creating a temporary ! Swarm class without any context (activateIn: nil) and then ! activating the startupSchedule in there, so it runs "doWarmupStep" ! 501 steps, but none of the 501 steps count against time in the ! larger context of the model. ! ! ! As of ASM-2.2, I have gotten rid of that complicated setup. Instead ! of creating the phony swarm and activating the warmup schedule ! inside it, I created a method in ASMModelSwarm.m that carries out ! one time step's worth of warmup. And then I dumped 501 ! createActionTo methods on the startup schedule that execute the ! required startup steps. I've verified the results are numerically ! identical to the original model. And the scheduling is much easier ! to understand. ! ! After the warmUp, then an ActionGroup called "periodActions" comes ! to the forefront. The periodSchedule is a repeating schedule, which ! causes the periodActions to happen at every time step in the larger ! model. ! ! In ASM-2.0, there was another initial schedule called ! initPeriodSchedule. After looking at it for a long time, I ! concluded it was doing nothing necessary, it was basically just ! running the periodActions at time 0 only. We might as well just ! schedule that action at time 0 in the startupSchedule. I have ! verified that the model runs exactly the same (numerically ! identical). Now, as noted below, I think this step is logically ! unnecessary, but removing it changes the numerical path of the ! simulation, so I'm leaving it in for comparison. "*/ - createEnd { modelTime=0; ! return [super createEnd]; } + + /*"This is very vital. When the ASMModelSwarm is created, it needs to + * be told where to find many constants that determine how agents are + * created. This passes handles of objects that have the required + * data."*/ - setParamsModel: (ASMModelParams *) modelParams BF: (BFParams *) bfp { bfParams = bfp; *************** *** 27,88 **** return self; } ! -(int) getNumBFagents { ! return asmModelParams->numBFagents; } ! -(double) getInitialCash { return asmModelParams->initialcash; } ! ! -getAgentList { return agentList; } ! -(World *)getWorld { if (world == nil) printf("Empty world!"); return world; } ! ! -(Specialist *)getSpecialist { return specialist; } ! ! -(Output *)getOutput { return output; } ! -(long int) getModelTime { return modelTime; } ! -setBatchRandomSeed: (int)newSeed { asmModelParams->randomSeed = newSeed; return self; } ! //Build and initialize objects ! -buildObjects { int i; // fprintf(stderr, "numBFagents %d \n intrate %f \n baseline %f \n eta %f \n initvar %f \n", asmModelParams->numBFagents,asmModelParams->intrate, asmModelParams->baseline, asmModelParams->eta, asmModelParams->initvar); - asmModelParams->exponentialMAs = 1; - if(asmModelParams->randomSeed != 0) [randomGenerator setStateFromSeed: asmModelParams->randomSeed]; //pj: note I'm making this like other swarm apps. Same each time, new seeds only if precautions taken. --- 81,159 ---- return self; } ! - setOutputObject: (Output *) obj { ! output = obj; ! return self; } ! /*" Returns the number of BFagents, which is held in asmModelParams"*/ ! - (int) getNumBFagents ! { ! return asmModelParams->numBFagents; ! } ! ! /*" Returns the initialcash value, which is held in asmModelParams"*/ ! - (double) getInitialCash { return asmModelParams->initialcash; } ! /*" Returns a list that contains all the agents"*/ ! - getAgentList { return agentList; } ! /*" Returns a handle of the world object, the place where historical ! price/dividend information is maintained. It is also the place ! where the BFagents can retrieve information in bit string form."*/ ! - (World *)getWorld { if (world == nil) printf("Empty world!"); return world; } ! /*" Return a pointer to the Specialist object"*/ ! - (Specialist *)getSpecialist { return specialist; } ! /*" Return a pointer to an object of the Output class. Sometimes it is ! necessary for other classes to find out who the output record keeper ! is and send that class a message."*/ ! - (Output *)getOutput { return output; } ! /*" ! Returns the integer time-step of the current simulation. ! "*/ ! - (long int)getModelTime { return modelTime; } ! ! /*"The value of the randomSeed that starts the simulation will remain ! fixed, unless you change it by using this method"*/ ! - setBatchRandomSeed: (int)newSeed { asmModelParams->randomSeed = newSeed; return self; } ! /*"Build and initialize objects"*/ ! - buildObjects { int i; // fprintf(stderr, "numBFagents %d \n intrate %f \n baseline %f \n eta %f \n initvar %f \n", asmModelParams->numBFagents,asmModelParams->intrate, asmModelParams->baseline, asmModelParams->eta, asmModelParams->initvar); if(asmModelParams->randomSeed != 0) [randomGenerator setStateFromSeed: asmModelParams->randomSeed]; //pj: note I'm making this like other swarm apps. Same each time, new seeds only if precautions taken. *************** *** 115,140 **** [specialist setETA: asmModelParams-> eta]; [specialist setREA: asmModelParams-> rea]; [specialist setREB: asmModelParams->reb]; - [specialist setWorld: world]; specialist = [specialist createEnd]; - [output setWorld: world]; [output setSpecialist: specialist]; /* Initialize the agent modules and create the agents */ agentList = [List create: [self getZone]]; //create list for agents - - // if ((bfParams = - // [lispAppArchiver getWithZone: self key: "bfParams"]) == nil) - // raiseEvent(InvalidOperation, - // "Can't find the BFParam's parameters"); - // [bfParams init]; - [BFagent init]; [BFagent setBFParameterObject: bfParams]; - [BFagent setWorld: world]; //nowObject create the agents themselves --- 186,203 ---- [specialist setETA: asmModelParams-> eta]; [specialist setREA: asmModelParams-> rea]; [specialist setREB: asmModelParams->reb]; specialist = [specialist createEnd]; [output setWorld: world]; [output setSpecialist: specialist]; /* Initialize the agent modules and create the agents */ agentList = [List create: [self getZone]]; //create list for agents + /* Set class variables */ + [BFagent init]; [BFagent setBFParameterObject: bfParams]; [BFagent setWorld: world]; //nowObject create the agents themselves *************** *** 152,163 **** agent = [agent createEnd]; [agentList addLast: agent]; } ! ! //Give the specialist access to the agentList ! [specialist setAgentList: agentList]; return self; } - writeParams { if (asmModelParams != nil && bfParams != nil) --- 215,225 ---- agent = [agent createEnd]; [agentList addLast: agent]; } ! return self; } + /*"This triggers a writing of the model parameters, for record keeping."*/ - writeParams { if (asmModelParams != nil && bfParams != nil) *************** *** 166,194 **** } ! //Create the model actions, separating into two different action groups, the ! //warmup period and the actual period. Note that time is not calculated ! //by a t counter but internally within Swarm. Time is recovered by the ! //getTime message ! -buildActions { [super buildActions]; ! warmupActions = [ActionGroup create: [self getZone]]; ! ! //Set the new dividend. This method is defined below. ! [warmupActions createActionTo: self ! message: M(warmupStepDividend)]; ! ! // Update world -- moving averages, bits, etc ! [warmupActions createActionTo: world ! message: M(updateWorld)]; ! ! //Fake price setting (crude fundamental value) ! [warmupActions createActionTo: self ! message: M(warmupStepPrice)]; ! ! //Define the actual period's actions. periodActions = [ActionGroup create: [self getZone]]; //Set the new dividend. This method is defined below. --- 228,243 ---- } ! /*"Create the model actions, separating into two different action ! * groups, the warmup period and the actual period. Note that time is ! * not calculated by a t counter but internally within Swarm. Time is ! * recovered by the getTime message"*/ ! ! - buildActions { [super buildActions]; ! //Define the actual period's actions. periodActions = [ActionGroup create: [self getZone]]; //Set the new dividend. This method is defined below. *************** *** 203,212 **** [periodActions createActionTo: world message: M(updateWorld)]; - // Tell BFagent class to prepare for trading - //pj: [periodActions createActionTo: self - //pj: message: M(prepareBFagentForTrading)]; - // Tell BFagents to get ready for trading (they may run GAs here) [periodActions createActionForEach: agentList message: M(prepareForTrading)]; --- 252,257 ---- *************** *** 218,330 **** // Complete the trades -- change agents' position, cash, and profit [periodActions createActionTo: specialist ! message: M(completeTrades)]; // Tell the agents to update their performance [periodActions createActionForEach: agentList message: M(updatePerformance)]; // Create the model schedule - warmupSchedule = [Schedule createBegin: [self getZone]]; - [warmupSchedule setRepeatInterval: 1]; - warmupSchedule = [warmupSchedule createEnd]; - [warmupSchedule at: 0 createAction: warmupActions]; - - startupSchedule = [Schedule create: [self getZone]]; - [startupSchedule at: 0 createActionCall: - (func_t)warmUp: warmupSchedule]; - - initPeriodSchedule = [Schedule createBegin: [self getZone]]; - [initPeriodSchedule setRepeatInterval: 1]; - initPeriodSchedule = [initPeriodSchedule createEnd]; - [initPeriodSchedule at: 0 createAction: periodActions]; - - [startupSchedule at: 0 createActionCall: - (func_t)initPeriod: initPeriodSchedule]; - - periodSchedule = [Schedule createBegin: [self getZone]]; - [periodSchedule setRepeatInterval: 1]; - periodSchedule = [periodSchedule createEnd]; - [periodSchedule at: 0 createAction: periodActions]; - return self; - } - void warmUp (id warmupSchedule) - { - id warmupSwarm; - id warmupActivity; - id terminateSchedule; - - warmupSwarm = [Swarm create: globalZone]; - [warmupSwarm activateIn: nil]; - warmupActivity = [warmupSchedule activateIn: warmupSwarm]; - - terminateSchedule = [Schedule create: globalZone]; - [terminateSchedule activateIn: warmupSwarm]; - [terminateSchedule at: 501 createActionTo: warmupActivity - message: M(terminate)]; - - while ([[warmupSwarm getSwarmActivity] run] != Completed); - } - - void initPeriod (id initPeriodSchedule) - { - id warmupSwarm; - id initPeriodActivity; - id terminateSchedule; - - warmupSwarm = [Swarm create: globalZone]; - [warmupSwarm activateIn: nil]; - initPeriodActivity = [initPeriodSchedule activateIn: warmupSwarm]; - - terminateSchedule = [Schedule create: globalZone]; - [terminateSchedule activateIn: warmupSwarm]; - [terminateSchedule at: 0 createActionTo: initPeriodActivity - message: M(terminate)]; - - while ([[warmupSwarm getSwarmActivity] run] != Completed); - } - ! -warmupStepDividend ! { ! [world setDividend: [dividendProcess dividend]]; ! return self; ! } ! -warmupStepPrice { ! // fprintf(stderr," Dividend %f \n", [world getDividend]); ! // fprintf(stderr," setPrice %f \n", [world getDividend]/asmModelParams->intrate ); ! ! // fprintf(stderr, "numBFagents %d \n intrate %f \n baseline %f \n eta %f \n initvar %f \n", ! // asmModelParams->numBFagents,asmModelParams->intrate, asmModelParams->baseline, asmModelParams->eta, asmModelParams->initvar); ! ! [world setPrice: ([world getDividend]/(double)asmModelParams->intrate )]; return self; ! } ! ! -periodStepDividend { modelTime++; [world setDividend: [dividendProcess dividend]]; return self; } ! -periodStepPrice { ! [world setPrice: [specialist performTrading]]; return self; } ! ! -activateIn: (id)swarmContext { [super activateIn: swarmContext]; [startupSchedule activateIn: self]; --- 263,338 ---- // Complete the trades -- change agents' position, cash, and profit [periodActions createActionTo: specialist ! message: M(completeTrades:Market:):agentList:world]; // Tell the agents to update their performance [periodActions createActionForEach: agentList message: M(updatePerformance)]; // Create the model schedule + startupSchedule = [Schedule create: [self getZone] setAutoDrop: YES]; + + //force the system to do 501 "warmup steps" at the beginning of the + //startup Schedule. Note that, since these phony steps are just + //handled by telling classes to do the required steps, nothing fancy + //is required. + { + int i; + for (i = 0; i < 502; i++) + [startupSchedule at: 0 createActionTo: self message:M(doWarmupStep)]; + } + + //pj: 2001-10-30. This was in the original model, I don't know why, but + //taking it out changes the numerical results, so I'm leaving it in, + //even though it is not logically necessary. + [startupSchedule at: 0 createAction: periodActions]; + ! periodSchedule = [Schedule createBegin: [self getZone]]; ! [periodSchedule setRepeatInterval: 1]; ! periodSchedule = [periodSchedule createEnd]; ! [periodSchedule at: 0 createAction: periodActions]; + return self; + } ! /*"Ask the dividend object for a draw from the dividend distribution, then tell the world about it. Tell the world to do an update of to respond to the dividend. Then calculate the price the divident implies and insert it into the world"*/ ! -doWarmupStep { ! double div = [dividendProcess dividend]; ! [world setDividend: div]; ! [world updateWorld]; ! [world setPrice: (div/(double)asmModelParams->intrate )]; return self; ! } ! /*" Have the dividendProcess calculate a new dividend. Then tell the ! world about the dividendProcess output. Also this increment the ! modelTime variable"*/ ! - periodStepDividend { modelTime++; [world setDividend: [dividendProcess dividend]]; return self; } ! /*"Have the Specialist perform the trading process. Then tell the world about the price that resulted from the Specialist's action."*/ ! - periodStepPrice { ! [world setPrice: [specialist performTrading: agentList Market: world]]; return self; } ! /*"The activities of the ASMModelSwarm are brought into time-sync with ! higher level Swarm activities. Basically, each time the higher level ! takes a step, this one will too, and the higher one won't step again ! until this one is finished with its turn."*/ ! - activateIn: (id)swarmContext { [super activateIn: swarmContext]; [startupSchedule activateIn: self]; *************** *** 332,338 **** return [self getSwarmActivity]; } ! -(void) drop { [dividendProcess drop]; [world drop]; --- 340,346 ---- return [self getSwarmActivity]; } ! - (void)drop { [dividendProcess drop]; [world drop]; diff -cr ASM-20011125/ASMObserverSwarm.h ASM-2.2/ASMObserverSwarm.h *** ASM-20011125/ASMObserverSwarm.h Wed Oct 24 18:26:08 2001 --- ASM-2.2/ASMObserverSwarm.h Fri Nov 2 16:57:18 2001 *************** *** 4,44 **** #import #import "ASMModelSwarm.h" #import "ASMModelParams.h" #import @interface ASMObserverSwarm: GUISwarm { ! int displayFrequency; ! id displayActions; ! id displaySchedule; ! ASMModelSwarm *asmModelSwarm; ! BOOL writeParams; ! BOOL writeData; ! id priceGraph; ! id volumeGraph; ! id positionHisto; //Histo *cashHisto; //A histogram for agent cash holdings. ! id relativeWealthHisto; //This is for comparing different agents. But since there is //currently only one agent this is not implemented. ! id deviationGraph; ! id deviationAverager; ! id deviationData; ! id deviationGrapher; ! ! //double *position; ! // double *wealth; ! //double * cash; ! //double *relativeWealth; } + createBegin: aZone; --- 4,41 ---- #import #import "ASMModelSwarm.h" #import "ASMModelParams.h" + #import "Output.h" #import @interface ASMObserverSwarm: GUISwarm { ! int displayFrequency; /*"update frequency for graphs"*/ ! id displayActions; /*"set of actions necessary to keep display up to date"*/ ! id displaySchedule; /*"Schedule that causes the displayActions to be carried out"*/ ! Output * output; /*"An output object"*/ ! ASMModelSwarm *asmModelSwarm; /*"Instance of ASMModelSwarm, where agents and the world are created and scheduled"*/ ! BOOL writeParams; /*"Indicator that files including parameter values should be written"*/ ! BOOL writeData;/*"Indicator that files including output values should be written"*/ ! id priceGraph; /*"Time plot of risk neutral and observed market price"*/ ! id volumeGraph; /*"Time plot of market trading volume"*/ ! id positionHisto;/*"Histogram showing amount of stock held by each individual agent"*/ //Histo *cashHisto; //A histogram for agent cash holdings. ! id relativeWealthHisto;/*"Histogram showing wealth of agents"*/ //This is for comparing different agents. But since there is //currently only one agent this is not implemented. ! id deviationGraph; /*"As of ASM-2.0, this was commented out in ASMObserverSwarm.m"*/ ! id deviationAverager; /*"ditto"*/ ! id deviationData;/*"ditto"*/ ! id deviationGrapher;/*"ditto"*/ } + createBegin: aZone; diff -cr ASM-20011125/ASMObserverSwarm.m ASM-2.2/ASMObserverSwarm.m *** ASM-20011125/ASMObserverSwarm.m Wed Oct 24 18:26:08 2001 --- ASM-2.2/ASMObserverSwarm.m Sat Nov 3 13:55:36 2001 *************** *** 4,11 **** --- 4,30 ---- #include + @implementation ASMObserverSwarm + /*" The ASMObserverSwarm is a Swarm with a graphical user interface + (GUI). It follows the prototype Swarm model, in that the Observer + Swarm is thought of an entity that can describe or report on the + state of a simulation, but not interact with it. The Observer + causes the ASMModelSwarm to be created, and it monitors various + variables by checking directly with the agents. + + Note that the ASMObserverSwarm has a set of "standard" methods that + Swarms have--buildObjects:, buildActions, activateIn:--and inside + each one it makes sure that the next-lower level, the ASMModelSwarm, + is sent the same message. + + If you don't want to watch the GUI, run the model in batch mode, + meaning you use the -b flag on the command line. + + "*/ + + + createBegin: aZone { ASMObserverSwarm *obj; *************** *** 42,47 **** --- 61,68 ---- } #if 0 + + /*"This is a legacy method. We need to update Swarm if it is ever to work again"*/ - printPriceGraph { [globalTkInterp eval: *************** *** 51,56 **** --- 72,78 ---- } + /*"This is a legacy method. We need to update Swarm if it is ever to work again"*/ - printVolumeGraph { [globalTkInterp eval: *************** *** 59,64 **** --- 81,87 ---- return self; } + /*"This is a legacy method. We need to update Swarm if it is ever to work again"*/ - printDeviationGraph { [globalTkInterp eval: *************** *** 67,73 **** return self; } ! - printRelWealthHisto { [globalTkInterp eval: --- 90,96 ---- return self; } ! /*"This is a legacy method. We need to update Swarm if it is ever to work again"*/ - printRelWealthHisto { [globalTkInterp eval: *************** *** 82,105 **** return [super createEnd]; } ! - buildObjects { int numagents; ASMModelParams * asmModelParams = [(id)arguments getModelParams]; BFParams * bfParams = [(id)arguments getBFParams]; ! ! [super buildObjects]; ! ! asmModelSwarm = [ASMModelSwarm create: self]; ! [asmModelSwarm setParamsModel: asmModelParams BF: bfParams]; - CREATE_ARCHIVED_PROBE_DISPLAY (asmModelParams); CREATE_ARCHIVED_PROBE_DISPLAY (self); ! ! [controlPanel setStateStopped]; [asmModelSwarm buildObjects]; numagents = asmModelParams->numBFagents; --- 105,138 ---- return [super createEnd]; } ! /*"This creates the model swarm, and then creates a number of ! monitoring objects, such a graphs which show the price trends, ! volume of stock trade, and some excellent bar charts which show the ! holdings and wealth of the agents. These bar charts (histograms) ! are available in very few other Swarm programs and if you want to ! know how it can be done, feel free to take a look!"*/ - buildObjects { int numagents; + + //need to create output and parameters so they exist from beginning ASMModelParams * asmModelParams = [(id)arguments getModelParams]; BFParams * bfParams = [(id)arguments getBFParams]; ! output = [[Output createBegin: self] createEnd]; ! [super buildObjects]; ! asmModelSwarm = [ASMModelSwarm create: self]; ! [asmModelSwarm setOutputObject: output]; CREATE_ARCHIVED_PROBE_DISPLAY (self); ! CREATE_ARCHIVED_PROBE_DISPLAY (asmModelParams); ! CREATE_ARCHIVED_PROBE_DISPLAY (bfParams); [controlPanel setStateStopped]; + + // Don't set the parameter objects until the model starts up That + // way, any changes typed into the gui will be taken into account by + // the model. + [asmModelSwarm setParamsModel: asmModelParams BF: bfParams]; [asmModelSwarm buildObjects]; numagents = asmModelParams->numBFagents; *************** *** 188,193 **** --- 221,228 ---- return self; } + + /*"This method is needed to stop run-time hangs when users close graph windows by clicking on their system's window close button"*/ - priceGraphDeath_ : caller { [priceGraph drop]; *************** *** 195,200 **** --- 230,236 ---- return self; } + /*"This method is needed to stop run-time hangs when users close graph windows by clicking on their system's window close button"*/ - volumeGraphDeath_ : caller { [volumeGraph drop]; *************** *** 202,207 **** --- 238,246 ---- return self; } + /*" This method gathers data about the agents, puts it into arrays, + and then passes those arrays to the histogram objects. As soon as we + tell the histograms to draw themselves, we will see the result"*/ - updateHistos { id index; *************** *** 229,284 **** return self; } ! - writeSimulationParams { writeParams = 1; ! [[asmModelSwarm getOutput] writeParams: [(id) arguments getModelParams] BFAgent: [(id) arguments getBFParams] Time: [asmModelSwarm getModelTime]]; ! return self; } ! - expostParamWrite { - // [asmModelSwarm initOutputForParamWrite]; if (writeParams == 1) ! [[asmModelSwarm getOutput] writeParams: [(id) arguments getModelParams] BFAgent: [(id) arguments getBFParams] Time: getCurrentTime()]; return self; } ! - (BOOL)ifParamWrite { return writeParams; } ! -(BOOL) toggleDataWrite ! { ! if(writeData != YES) ! { ! [[asmModelSwarm getOutput] prepareOutputFile]; ! writeData = YES; ! } ! else ! writeData = NO; ! return writeData; } ! - _writeRawData_ { if (writeData == YES) ! [[asmModelSwarm getOutput] writeData]; ! // fprintf(stderr,"getcurrent %ld modeltime %ld",getCurrentTime(),[asmModelSwarm getModelTime]); return self; } ! - buildActions { [super buildActions]; --- 268,330 ---- return self; } ! /*"This causes the system to save a copy of the current parameter ! settings. It can be turned on by hitting a button in the probe ! display that shows at the outset of the model run, or any time ! thereafter."*/ - writeSimulationParams { writeParams = 1; ! [output writeParams: [(id) arguments getModelParams] BFAgent: [(id) arguments getBFParams] Time: [asmModelSwarm getModelTime]]; ! return self; } ! /*"If the writeParams variable is set to YES, then this method cause ! the system to save a snapshot of the parameters after the system's ! run ends."*/ - expostParamWrite { if (writeParams == 1) ! [output writeParams: [(id) arguments getModelParams] BFAgent: [(id) arguments getBFParams] Time:[asmModelSwarm getModelTime]]; return self; } ! /*"Returns the condition of the writeParams variable, an indicator ! that parameters should be written to files"*/ - (BOOL)ifParamWrite { return writeParams; } + /*"This toggles data writing features. It can be accessed by punching + a button in a probe display that is shown on the screen when the simulation begins"*/ ! -(BOOL)toggleDataWrite { ! if(writeData != YES) ! { ! [output prepareOutputFile]; ! writeData = YES; ! } ! else writeData = NO; ! return writeData; } ! /*"If data logging is turned on, this cause data to be written whenever it is called"*/ - _writeRawData_ { if (writeData == YES) ! [output writeData]; return self; } ! /*" Create actions and schedules onto which the actions are put. ! Since this is an observer, the actions are intended to make sure ! data is collected, displayed to the screen, and written to files ! where appropriate"*/ - buildActions { [super buildActions]; *************** *** 307,312 **** --- 353,364 ---- return self; } + + /*"This method activates the model swarm within the context of this + observer, and then it activated the observer's schedules. This + makes sure that the actions inserted at time t inside the model are + placed into the overall time sequence before the observer scheduled + actions that update graphs which describe the results"*/ - activateIn: swarmContext { [super activateIn: swarmContext]; *************** *** 317,329 **** return [self getSwarmActivity]; } -(void) drop { [asmModelSwarm drop]; [super drop]; } @end - - --- 369,386 ---- return [self getSwarmActivity]; } + + /*"In order to make sure that the data is actually written to disk, it + is necessary to pass a "drop" message down the hierarchy so that all + data writers know it is time to finish up their work. This drop + method is called at the end of the main.m file and it propogates + down to all objects created in asmModelSwarm"*/ -(void) drop { + [self expostParamWrite]; [asmModelSwarm drop]; [super drop]; } @end diff -cr ASM-20011125/Agent.h ASM-2.2/Agent.h *** ASM-20011125/Agent.h Thu May 25 13:22:37 2000 --- ASM-2.2/Agent.h Fri Oct 26 11:46:52 2001 *************** *** 5,15 **** @interface Agent:SwarmObject { @public ! double demand; // bid or -offer ! double profit; // exp-weighted moving average ! double wealth; // total agent wealth ! double position; // total shares of stock ! double cash; // total agent cash position double initialcash; double minholding; double mincash; --- 5,15 ---- @interface Agent:SwarmObject { @public ! double demand; /*" bid or -offer"*/ ! double profit; /*" exp-weighted moving average "*/ ! double wealth; /*" total agent wealth "*/ ! double position; /*" total shares of stock "*/ ! double cash; /*" total agent cash position "*/ double initialcash; double minholding; double mincash; diff -cr ASM-20011125/Agent.m ASM-2.2/Agent.m *** ASM-20011125/Agent.m Mon Apr 24 20:25:06 2000 --- ASM-2.2/Agent.m Fri Oct 26 11:46:52 2001 *************** *** 1,96 **** - // This is the abstract superclass of all agent classes; all agent classes - // must be direct or indirect descendants of this one. - // CLASS METHODS // +setWorld: (World *)aWorld ! // Sets a world for each type of agent. It is a class method as it ! // is used in both class and instance methods in BFagent. ! // ! // PUBLIC INSTANCE METHODS, NOT USUALLY OVERRIDDEN ! // -setID: (int)iD ! // Gives an integer name to an agent during creation. ! // ! // -setPosition: (double)aDouble ! // Sets the agent's position (holding) to "aDouble". ! // ! // -setintrate: (double)rate ! // Sets intrate and intratep1 (intrate + 1). ! // ! // -setminHolding: (double)holding minCash: (double)minimumcash ! // Sets the borrowing and short selling constraints, i.e., the ! // values can be negative. ! // ! // -setInitialCash ! // Sets the initial cash holdings of each agent. ! // ! // -setInitialHoldings ! // Sets the initial stock holdings of each agent. ! // ! // -getPriceFromWorld ! // Sets an instance variable of agent, price, to the current price ! // which is controlled by the World. ! // ! // -getDividendFromWorld ! // Same deal as getPriceFromWorld but with dividend. ! // ! // -creditEarningsAndPayTaxes ! // Sent to each agent after a dividend is declared. The ! // agents receive the dividend for each unit of stock they hold. ! // Their "cash" in the fixed asset account also receives its ! // interest. Then taxes are charged on the previous total wealth, ! // at a rate that balances the interest on cash -- so if the agent ! // had everything in cash it would end up at the same place. ! // ! // -(double)constrainDemand: (double *)slope : (double)trialprice ! // Checks "demand" against the mincash and minholding constraints ! // and clips it if necessary, then also setting *slope. For use ! // within subclass implementations of getDemandAndSlope: forPrice:. ! // Used only by agents that work with the Slope Specialist. ! // ! // -(double)getAgentPosition, -(double)getWealth, -(double)getCash ! // Returns an agent's stock holding, wealth, and cash. These are ! // updated in Specialist. ! // ! // PUBLIC INSTANCE METHODS, SPECIFIED BY SUBCLASSES ! // -prepareForTrading ! // Sent to each enabled agent at the start of each trading period, ! // before the first -getDemandAndSlope:forPrice: message for that ! // agent. The class method +prepareForTrading: is sent to each type ! // before any of these messages. ! // ! // -(double)getDemandandSlope: (double *)slope forPrice: (double)p ! // Sent to each agent during bidding to ask for its bid ! // (demand > 0) or offer (demand < 0) at price p. The agent may ! // also return a value for d(demand)/d(price) through "slope", but ! // this is not required; *slope may be left unchanged. This method ! // may be called one or more times in each period, depending on the ! // specialist method. The last such call is at the final trading ! // price. The -prepareForTrading message is sent to each agent ! // before the first such call in each period. Note that agents without ! // demand functions return zero slope, but the Slope specialist is never ! // used with these agents. ! // ! // There is no default for this method in the Agent class; the agent ! // subclasses MUST provide it. ! // ! // -updatePerformance ! // Sent to each enabled agent at the end of each period to tell it to ! // update its performance meaures, forecasts, etc. ! // ! // VARIABLES AND PARAMETERS CALLED FROM OTHER OBJECTS ! // ! // double price, dividend ! // Market variables from World. ! // ! // double intrate, mincash, initialcash, minholding ! // Market constants from ASMModelSwarm. ! // ! // Variables Called by others from the agent ! // double demand; /* bid or -offer */ ! // double profit; /* exp-weighted moving average */ ! // double wealth; /* total agent wealth */ ! // double position; /* total shares of stock */ ! // double cash; /* total agent cash position */ #import "Agent.h" --- 1,6 ---- // CLASS METHODS // +setWorld: (World *)aWorld ! #import "Agent.h" *************** *** 98,111 **** World * worldForAgent; @implementation Agent +setWorld: (World *)aWorld; { worldForAgent = aWorld; return self; } ! -setID: (int)iD { myID = iD; --- 8,28 ---- World * worldForAgent; @implementation Agent + /*" This is the abstract superclass of all agent classes; all agent classes + // must be direct or indirect descendants of this one. + "*/ + + /*" Sets a world for an agent. It is a class method as it is used in + both class and instance methods in BFagent."*/ +setWorld: (World *)aWorld; { worldForAgent = aWorld; return self; } ! /*" Gives an integer name to an agent during creation. Sometimes it ! * helps with debugging to have a unique id for each agent "*/ -setID: (int)iD { myID = iD; *************** *** 113,118 **** --- 30,36 ---- } + /*" Sets the agent's position (holding) to "aDouble"."*/ -setPosition: (double)aDouble { position = aDouble; *************** *** 120,125 **** --- 38,44 ---- } + /*" Sets the IVAR intrate and uses that to calculate intratep1 (intrate + 1)."*/ -setintrate: (double)rate; { intrate = rate; *************** *** 128,133 **** --- 47,54 ---- } + /*" Sets the borrowing and short selling constraints, i.e., the + // values can be negative. It sets values of the IVARS minholding and mincash"*/ -setminHolding: (double)holding minCash: (double)minimumcash { minholding = holding; *************** *** 135,155 **** return self; } ! -setInitialCash: (double)initcash; { initialcash = initcash; return self; } ! -setInitialHoldings - /* - * Designated initializer. Most agent classes will have additional - * initialization, but should do [super setInitialHoldings] to run this first. - */ { - // Initialize instance variables common to all agents profit = 0.0; wealth = 0.0; cash = initialcash; --- 56,76 ---- return self; } ! /*" Sets the initial cash holdings of each agent."*/ -setInitialCash: (double)initcash; { initialcash = initcash; return self; } ! /*" Sets the initial stock holdings of each agent. It is the ! * designated initializer. Most agent classes will have additional ! * initialization, but should do [super setInitialHoldings] to run ! * this first. It will initialize instance variables common to all ! * agents, setting profit,wealth, and position equal to 0, and it sets ! * the variable cash equal to initialcash "*/ -setInitialHoldings { profit = 0.0; wealth = 0.0; cash = initialcash; *************** *** 158,171 **** return self; } ! -getPriceFromWorld { price = [worldForAgent getPrice]; return self; } ! -getDividendFromWorld { dividend = [worldForAgent getDividend]; --- 79,94 ---- return self; } ! /*" Sets an instance variable of agent, price, to the current price ! which is controlled by the object known as "world". Please note this ! assumes world is already set. "*/ -getPriceFromWorld { price = [worldForAgent getPrice]; return self; } ! /*"Sets an instance variable of agent, dividend, to the current dividend. That information is retrieved from the object known as "world"."*/ -getDividendFromWorld { dividend = [worldForAgent getDividend]; *************** *** 174,180 **** -creditEarningsAndPayTaxes ! /* * This is done in each period after the new dividend is declared. It is * not normally overridden by subclases. The taxes are assessed on the * previous wealth at a rate so that there's no net effect on an agent --- 97,109 ---- -creditEarningsAndPayTaxes ! /*"Sent to each agent after a dividend is declared. The agents ! * receive the dividend for each unit of stock they hold. Their cash" ! * in the fixed asset account also receives its interest. Then taxes ! * are charged on the previous total wealth, at a rate that balances ! * the interest on cash -- so if the agent had everything in cash it ! * would end up at the same place. ! * This is done in each period after the new dividend is declared. It is * not normally overridden by subclases. The taxes are assessed on the * previous wealth at a rate so that there's no net effect on an agent *************** *** 186,192 **** * cash -= wealth*intrate; // taxes * but we cut directly to the cash: * cash -= (price*intrate - dividend)*position ! */ { [self getPriceFromWorld]; [self getDividendFromWorld]; --- 115,121 ---- * cash -= wealth*intrate; // taxes * but we cut directly to the cash: * cash -= (price*intrate - dividend)*position ! " */ { [self getPriceFromWorld]; [self getDividendFromWorld]; *************** *** 204,213 **** -(double)constrainDemand: (double *)slope : (double)trialprice ! /* ! * Method used by agents to constrain their demand according to the * mincash and minholding constraints. ! */ { // If buying, we check to see if we're within borrowing limits, // remembering to handle the problem of negative dividends - --- 133,147 ---- -(double)constrainDemand: (double *)slope : (double)trialprice ! /*" Method used by agents to constrain their demand according to the * mincash and minholding constraints. ! ! * It checks "demand" against the ! * mincash and minholding constraints and clips it if necessary, then ! * also setting *slope. For use within subclass implementations of ! * getDemandAndSlope: forPrice:. Used only by agents that work with ! * the Slope Specialist."*/ ! { // If buying, we check to see if we're within borrowing limits, // remembering to handle the problem of negative dividends - *************** *** 237,266 **** return demand; } ! -(double)getAgentPosition { return position; } ! -(double)getWealth { return wealth; } -(double)getCash { return cash; } ! -prepareForTrading { ! return self; // default code does nothing } -(double)getDemandAndSlope: (double *)slope forPrice: (double)p { --- 171,219 ---- return demand; } ! /*" Return the agent's current position "*/ -(double)getAgentPosition { return position; } ! /*" Return the agent's current wealth "*/ -(double)getWealth { return wealth; } + /*" Return the agent's current cash level "*/ -(double)getCash { return cash; } ! /*"Sent to each enabled agent at the start of each trading period, ! * before the first -getDemandAndSlope:forPrice: message for that ! * agent. The class method +prepareForTrading: is sent to each type ! * before any of these messages. This probably should have some ! * subclassMustImplement code here, because the default code does ! * nothing. It must be overridden"*/ -prepareForTrading { ! return self; } + /*" This message is sent to each agent during bidding to ask for its bid + // (demand > 0) or offer (demand < 0) at price p. The agent may + // also return a value for d(demand)/d(price) through "slope", + // but this is not required; *slope may be left unchanged. This + // method may be called one or more times in each period, + // depending on the specialist method. The last such call is at + // the final trading price. The -prepareForTrading message is + // sent to each agent before the first such call in each period. + // Note that agents without demand functions return zero slope, + // but the Slope specialist is never used with these agents. + // + // There is no default for this method in the Agent class; the + // agent subclasses MUST provide it."*/ -(double)getDemandAndSlope: (double *)slope forPrice: (double)p { *************** *** 268,277 **** return 0.0; // not reached } ! -updatePerformance { ! return self; // default code does nothing, specified by each agent type } --- 221,233 ---- return 0.0; // not reached } ! /*"Sent to each enabled agent at the end of each period to tell it to ! // update its performance meaures, forecasts, etc. The default code ! does nothing, this method must be specified by each agent type. ! Probably needs a subclass responsibility statement"*/ -updatePerformance { ! return self; } diff -cr ASM-20011125/BFCast.h ASM-2.2/BFCast.h *** ASM-20011125/BFCast.h Fri Jun 2 11:03:37 2000 --- ASM-2.2/BFCast.h Thu Nov 1 21:42:12 2001 *************** *** 5,123 **** @interface BFCast: SwarmObject { ! double forecast; // this forecast of return ! double lforecast; // previous forecast ! double variance; // variance of this forecast ! double strength; ! double a; // (price + dividend) coefficient ! double b; // dividend coefficient ! double c; // constant term ! double specfactor; // specificity factor; strength=specfactor/variance ! double bitcost; ! ! // struct BF_fcast *next; // linked list of ACTIVE forecasts ! // struct BF_fcast *lnext; ! BitVector *conditions; ! int lastactive; ! int specificity; ! int count; ! int condwords; ! int condbits; ! int nnulls; }; - createEnd; ! -(void) incrSpecificity; ! -(void) decrSpecificity; ! -(void) setSpecificity: (int) specificity; ! -(int) getSpecificity; ! -(void) setConditions: (int *) x; ! -(int *) getConditions; ! -(BitVector *) getConditionsObject; ! - (void) setNNulls: (int) x; ! - (void) setBitcost: (double) x; ! -(void) setConditionsWord: (int) i To: (int) value; ! -(int) getConditionsWord: (int) x; ! -(void) setConditionsbit: (int) bit To: (int) x; //works for 0,1,2 ! -(void) setConditionsbit: (int) bit FromZeroTo: (int) x;//faster if cond[bit]=0 ! -(void) maskConditionsbit: (int) bit; ! -(void) switchConditionsbit: (int) bit; ! -(int) getConditionsbit: (int)bit; ! -(void) setAval: (double) x; ! -(void) setBval: (double) x; ! -(void) setCval: (double) x; ! - (double) getAval; ! - (double) getBval; ! - (double) getCval; ! -(void) updateSpecfactor; ! -(void) setSpecfactor: (double) x; ! - (double) getSpecfactor; ! - (void) setVariance: (double) x; ! -(double) getVariance; ! -(void) setCondwords: (int) x; ! -(void) setCondbits: (int) x; ! -(void) setForecast: (double) x; ! - (double) getForecast; ! -(double) updateForecastPrice: (double) price Dividend: (double) dividend; ! -(void) setLforecast: (double) x; ! - (double) getLforecast; ! -(void) setLastactive: (int) x; ! -(int) getLastactive; ! -(void) setCnt: (int) x; ! -(int) getCnt; ! - (int) incrCount; ! -(void) setStrength: (double) x; ! - (double) getStrength; ! - copyEverythingFrom: (BFCast *) from; - print; ! - printcond: (int) conditions; ! -(void) drop; @end --- 5,121 ---- @interface BFCast: SwarmObject { ! double forecast; /*" this forecast of return"*/ ! double lforecast; /*" previous forecast"*/ ! double variance; /*" variance of this forecast"*/ ! double strength; /*"strength=maxdev - variance +specfactor. This was the original sfsm specification, not the ASM-2.0 specification"*/ ! double a; /*" (price + dividend) coefficient"*/ ! double b; /*" dividend coefficient "*/ ! double c; /*" constant term"*/ ! double specfactor; /*" specificty=(condbits - nnulls - specificity)* bitcost. "*/ ! double bitcost; /*" cost of using bits in forecasts"*/ ! ! BitVector *conditions; /*" a BitVector object that holds records on which conditions in the world are being monitored by this forecast object"*/ ! int lastactive; /*" last time period in which this forecast was active"*/ ! int specificity; /*" specificity "*/ ! int count; /*" number of times this forecast has been active"*/ ! int condwords; /*"number of words of memory needed to hold the conditions"*/ ! int condbits; /*"number of bits of information monitored by this forecast"*/ ! int nnulls; /*"number of 'unused' bits that remain in the allocated vector after the 'condbits' bits have been used"*/ }; - createEnd; ! - (void)incrSpecificity; ! - (void)decrSpecificity; ! - (void)setSpecificity: (int)specificity; ! - (int)getSpecificity; ! - (void)setConditions: (int *) x; ! - (int *)getConditions; ! - (BitVector *)getConditionsObject; ! - (void)setNNulls: (int)x; ! - (void)setBitcost: (double)x; ! - (void)setConditionsWord: (int)i To: (int)value; ! - (int)getConditionsWord: (int)x; ! - (void)setConditionsbit: (int)bit To: (int)x; //works for 0,1,2 ! - (void)setConditionsbit: (int)bit FromZeroTo: (int)x;//faster if cond[bit]=0 ! - (void)maskConditionsbit: (int)bit; ! - (void)switchConditionsbit: (int)bit; ! - (int) getConditionsbit: (int)bit; ! - (void) setAval: (double)x; ! - (void) setBval: (double)x; ! - (void) setCval: (double)x; ! - (double)getAval; ! - (double)getBval; ! - (double)getCval; ! - (void)updateSpecfactor; ! - (void)setSpecfactor: (double)x; ! - (double)getSpecfactor; ! - (void)setVariance: (double) x; ! - (double)getVariance; ! - (void)setCondwords: (int)x; ! - (void)setCondbits: (int)x; ! - (void)setForecast: (double)x; ! - (double)getForecast; ! - (double)updateForecastPrice: (double)price Dividend: (double)dividend; ! - (void)setLforecast: (double)x; ! - (double)getLforecast; ! - (void)setLastactive: (int)x; ! - (int)getLastactive; ! - (void)setCnt: (int)x; ! - (int)getCnt; ! - (int)incrCount; ! - (void)setStrength: (double)x; ! - (double)getStrength; ! - copyEverythingFrom: (BFCast *)from; - print; ! - printcond: (int)conditions; ! - (void)drop; @end diff -cr ASM-20011125/BFCast.m ASM-2.2/BFCast.m *** ASM-20011125/BFCast.m Wed Feb 7 15:30:52 2001 --- ASM-2.2/BFCast.m Sat Nov 3 13:55:36 2001 *************** *** 3,8 **** --- 3,16 ---- #import @implementation BFCast + /*"A BFCast is an object that holds all the forcecasting components of + a bit forecast. That means it has a BitVector (a thing called + "conditions" in the code that keeps track of which world bits are + being monitored) as well as other coefficients that are used to + calculate forecasts. It has instance variables that record when the + rule was last used, how many times it has been used, how accururate + its predictions are, and so forth."*/ + - createEnd { *************** *** 20,277 **** return self; } + init { [BitVector init]; return self; } ! -(void) drop { [conditions drop]; [super drop]; } ! ! -(void) setCondwords: (int) x { condwords = x; } ! -(void) setCondbits: (int) x { ! condbits=x; } - (void) setNNulls: (int) x { ! nnulls= nnulls; } - (void) setBitcost: (double) x { bitcost = x; } ! ! -(void) setConditions: (int *) x { [conditions setConditions: x]; } ! -(int *) getConditions { return [conditions getConditions]; } ! ! -(BitVector *) getConditionsObject { return conditions; } ! -(void) setConditionsWord: (int) i To: (int) value { [conditions setConditionsWord: i To: value]; } ! ! -(int) getConditionsWord: (int) x { return [conditions getConditionsWord: x]; } ! ! -(void) setConditionsbit: (int) bit To: (int) x { [conditions setConditionsbit: bit To: x]; } ! ! -(void) setConditionsbit: (int) bit FromZeroTo: (int) x { [conditions setConditionsbit: bit FromZeroTo: x]; } ! ! -(int) getConditionsbit: (int)bit { return [conditions getConditionsbit: bit]; } ! -(void) maskConditionsbit: (int) bit { - // conditions[WORD(bit)] &= NMASK[bit]; - // specificity --; [conditions maskConditionsbit: bit]; } ! -(void) switchConditionsbit: (int) bit { ! // conditions[WORD(bit)] ^= MASK[bit]; [conditions switchConditionsbit: bit]; } ! -(void) setAval: (double) x { ! a=x; } ! -(void) setBval: (double) x { ! b=x; } ! -(void) setCval: (double) x { ! c=x; } ! ! - (double) getAval { return a; } ! ! - (double) getBval { return b; } ! - (double) getCval { return c; } ! -(void) updateSpecfactor { //was in BFagent: specfactor = 1.0/(1.0 + x*specificity); //but the bfagent.m way is so much nicer ! specfactor= (condbits - nnulls - specificity)* bitcost; //follows bfagent.m } ! -(void) setSpecfactor: (double) x { ! specfactor=x; }; ! ! - (double) getSpecfactor { return specfactor; } ! ! -(void) incrSpecificity { ++ specificity; } ! ! -(void) decrSpecificity { --specificity; } ! ! -(void) setSpecificity: (int) x { ! specificity=x; } ! -(int) getSpecificity { return specificity; } ! -(void) setVariance: (double) x { variance=x; } ! -(double) getVariance { return variance; } ! -(void) setLastactive: (int) x { ! lastactive=x; } ! -(int) getLastactive { return lastactive; } ! - (int) getCnt { return count; } ! -(void) setCnt: (int) x { count = x; } ! - (int) incrCount; { return ++count; } ! - (double) getStrength { return strength; } ! -(void) setStrength: (double) x { ! strength=x; } ! ! -(void) setLforecast: (double) x { ! lforecast=x; } ! - (double) getLforecast { return lforecast; } ! ! -(void) setForecast: (double) x { ! forecast=x; } ! - (double) getForecast { return forecast; } ! -(double) updateForecastPrice: (double) price Dividend: (double) dividend { ! lforecast=forecast; ! forecast= a* (price+dividend) + b*dividend + c; return forecast; } ! - copyEverythingFrom: (BFCast *) from { ! forecast= [from getForecast]; lforecast = [from getLforecast]; variance = [from getVariance]; strength = [from getStrength]; --- 28,323 ---- return self; } + /*"The init is needed because BitVector has to be told how bit of a + bit vector it will need"*/ + init { [BitVector init]; return self; } ! /*"Free dynamically allocated memory"*/ ! - (void)drop { [conditions drop]; [super drop]; } ! /*"Sets the number of words-worth's of conditions that are going to be used"*/ ! - (void)setCondwords: (int)x { condwords = x; } ! /*"Sets the number of bits. This is the number of aspect of the world that are monitored"*/ ! - (void)setCondbits: (int)x { ! condbits = x; } + /*"Null bits may be needed if the bit vector that is allocated is larger than the number of conditions being monitored. Since a bitvector allocates space in sizes of words, this might be important. Luckily, in the current design of ASM-2.0 (and after), there are no null bits."*/ - (void) setNNulls: (int) x { ! nnulls = nnulls; } + /*"Set the variable bitcost at x"*/ - (void) setBitcost: (double) x { bitcost = x; } ! /*"Rather than individually set bits one by one, we might want to set ! all of them at once. That means we pass in a pointer to an array of ! words that is the "right size" for all the bits."*/ ! - (void) setConditions: (int *)x { [conditions setConditions: x]; } ! /*"Returns a pointer to an array, the integer representation of the ! conditions"*/ ! - (int *)getConditions { return [conditions getConditions]; } ! /*"Returns an object of type BitVector, the represnetation of ! conditions that is actually used inside this class or ! calculations"*/ ! - (BitVector *)getConditionsObject { return conditions; } ! /*"For low level access to a full word's-worth of the condition"*/ ! - (void)setConditionsWord: (int)i To: (int)value { [conditions setConditionsWord: i To: value]; } ! /*"Returns the integer representation of the x'th word in the ! conditions"*/ ! - (int)getConditionsWord: (int)x { return [conditions getConditionsWord: x]; } ! /*"Sets the value of a bit in the conditions"*/ ! - (void)setConditionsbit: (int)bit To: (int)x { [conditions setConditionsbit: bit To: x]; } ! /*"If a bit is currently set to 0 ("don't care"), then change it to ! something else (1 or 2)"*/ ! - (void)setConditionsbit: (int)bit FromZeroTo: (int)x { [conditions setConditionsbit: bit FromZeroTo: x]; } ! /*"Returns 0,1,or 2, for a given bit in the conditions records"*/ ! - (int)getConditionsbit: (int)bit { return [conditions getConditionsbit: bit]; } ! - (void)maskConditionsbit: (int)bit { [conditions maskConditionsbit: bit]; } ! ! /*"Change a YES to a NO, and vice versa"*/ ! - (void)switchConditionsbit: (int)bit { ! // conditions[WORD(bit)] ^= MASK[bit]; [conditions switchConditionsbit: bit]; } ! /*"Set a coefficient from demand equation"*/ ! - (void)setAval: (double)x { ! a = x; } ! /*"Set b coefficient from demand equation"*/ ! - (void)setBval: (double)x { ! b = x; } ! /*"Set c coefficient from demand equation"*/ ! - (void)setCval: (double)x { ! c = x; } ! /*"Return a coefficient from demand equation"*/ ! - (double)getAval { return a; } ! /*"Return b coefficient from demand equation"*/ ! - (double)getBval { return b; } ! /*"Return c coefficient from demand equation"*/ ! - (double)getCval { return c; } ! /*"Update the spec factor of this forecast. That means calculate: ! specfactor= (condbits - nnulls - specificity)* bitcost ! "*/ ! - (void)updateSpecfactor { //was in BFagent: specfactor = 1.0/(1.0 + x*specificity); //but the bfagent.m way is so much nicer ! specfactor = (condbits - nnulls - specificity)* bitcost; //follows bfagent.m } ! /*"Set the specfactor value of this forecast"*/ ! - (void)setSpecfactor: (double)x { ! specfactor = x; }; ! /*"Return the specfactor value of this forecast"*/ ! - (double)getSpecfactor { return specfactor; } ! /*"Raise the specificity value of this forecast by one unit"*/ ! - (void)incrSpecificity { ++ specificity; } ! /*"Reduce the specificity value of this forecast by one unit"*/ ! - (void)decrSpecificity { --specificity; } ! /*"Set the specificity of this forecast"*/ ! - (void)setSpecificity: (int) x { ! specificity = x; } ! /*"Return the specificity of this forecast"*/ ! - (int)getSpecificity { return specificity; } ! /*"Set the variance of this forecast"*/ ! - (void)setVariance: (double)x { variance=x; } ! /*"Return the variance of this forecast"*/ ! - (double)getVariance { return variance; } ! /*"Set the time on which this forecast was last active to an input value"*/ ! - (void)setLastactive: (int)x { ! lastactive = x; } ! /*"Return the time on which this forecast was last active"*/ ! - (int)getLastactive { return lastactive; } ! /*"Return the value of count"*/ ! - (int)getCnt { return count; } ! /*"Set the count variable to an inputted value"*/ ! - (void)setCnt: (int)x { count = x; } ! /*"Increment this forecast's count variable"*/ ! - (int)incrCount; { return ++count; } ! /*"Return strength of this forecast"*/ ! - (double)getStrength { return strength; } ! /*"Set the strength value to an inputted value"*/ ! - (void) setStrength: (double)x { ! strength = x; } ! /*"Set the previous forecast of this object to an inputted value"*/ ! - (void)setLforecast: (double)x { ! lforecast = x; } ! /*"Get forecast from the previous time period"*/ ! - (double)getLforecast { return lforecast; } ! /*"Set the forecast of this object to an inputted value"*/ ! - (void) setForecast: (double) x { ! forecast = x; } ! /*"Return forecast from this object"*/ ! - (double)getForecast { return forecast; } ! /*"Calculate new forecast on basis of price and dividend information"*/ ! - (double) updateForecastPrice: (double)price Dividend: (double)dividend { ! lforecast = forecast; ! forecast = a* (price+dividend) + b*dividend + c; return forecast; } ! /*"Given an input forecast object, systematically ask it for ! all its IVARs and replace current settings with them."*/ ! - copyEverythingFrom: (BFCast *)from { ! forecast = [from getForecast]; lforecast = [from getLforecast]; variance = [from getVariance]; strength = [from getStrength]; *************** *** 286,291 **** --- 332,339 ---- return self; } + /*"Return some disgnostic information about the status of + variables inside this forecast to the terminal"*/ - print { int word; *************** *** 297,303 **** return self; } ! - printcond: (int) word { [conditions printcond: word]; return self; --- 345,351 ---- return self; } ! - printcond: (int)word { [conditions printcond: word]; return self; diff -cr ASM-20011125/BFParams.h ASM-2.2/BFParams.h *** ASM-20011125/BFParams.h Fri Jun 29 16:30:15 2001 --- ASM-2.2/BFParams.h Sat Nov 3 13:55:36 2001 *************** *** 18,64 **** @interface BFParams: SwarmObject { ! @public ! int numfcasts; ! int condwords; ! int condbits; ! int mincount; ! int gafrequency; ! int firstgatime; ! int longtime; // unused time before Generalize() int individual; double tauv; double lambda; double maxbid; double bitprob; ! double subrange; // fraction of min-max range for initial random values ! double a_min,a_max; // min and max for p+d coef ! double b_min,b_max; // min and max for div coef ! double c_min,c_max; // min and max for constant term ! double a_range,b_range,c_range; // derived: max - min ! double newfcastvar; // variance assigned to a new forecaster ! double initvar; // variance of overall forecast for t<200 ! double bitcost; // penalty parameter for specificity ! double maxdev; // max deviation of a forecast in variance estimation ! double poolfrac; // fraction of rules in replacement pool ! double newfrac; // fraction of rules replaced ! double pcrossover; // probability of running Crossover() at all. ! double plinear; // linear combination "crossover" prob. ! double prandom; // random from each parent crossover prob. ! double pmutation; // per bit mutation prob. ! double plong; // long jump prob. ! double pshort; // short (neighborhood) jump prob. ! double nhood; // size of neighborhood. ! double genfrac; // fraction of 0/1 bits to generalize ! double gaprob; // derived: 1/gafrequency ! int npool; // derived: replacement pool size ! int nnew; // derived: number of new rules ! int nnulls; ! int lastgatime; ! int *bitlist; // dynamic array, length condbits ! double *problist; // dynamic array, length condbits ! unsigned int *myworld;// dynamic array, length condwords ! int npoolmax ; /* size of reject array */ int nnewmax ; /* size of newfcast array */ int ncondmax; /* size of newc*/ --- 18,62 ---- @interface BFParams: SwarmObject { ! @public ! int numfcasts; /*"number of forecasts maintained by this agent"*/ ! int condwords; /*"number of words of memory required to hold bits"*/ ! int condbits; /*"number of conditions bits are monitored"*/ ! int mincount; /*"minimum number of times forecast must be used to become active"*/ ! int gafrequency; /*"how often is genetic algorithm done?"*/ ! int firstgatime; /*"after how many time steps is the genetic algorithm done"*/ ! int longtime; /*" unused time before Generalize() in genetic algorithm"*/ int individual; double tauv; double lambda; double maxbid; double bitprob; ! double subrange; /*" fraction of min-max range for initial random values"*/ ! double a_min,a_max; /*" min and max for p+d coef"*/ ! double b_min,b_max; /*" min and max for div coef"*/ ! double c_min,c_max; /*" min and max for constant term"*/ ! double a_range,b_range,c_range; /*" derived: max - min" */ ! double newfcastvar; /*" variance assigned to a new forecaster"*/ ! double initvar; /*" variance of overall forecast for t<200"*/ ! double bitcost; /*" penalty parameter for specificity"*/ ! double maxdev; /*" max deviation of a forecast in variance estimation"*/ ! double poolfrac; /*" fraction of rules in replacement pool"*/ ! double newfrac; /*" fraction of rules replaced"*/ ! double pcrossover; /*" probability of running Crossover()."*/ ! double plinear; /*" linear combination "crossover" prob."*/ ! double prandom; /*" random from each parent crossover prob."*/ ! double pmutation; /*" per bit mutation prob."*/ ! double plong; /*" long jump prob."*/ ! double pshort; /*" short (neighborhood) jump prob."*/ ! double nhood; /*" size of neighborhood."*/ ! double genfrac; /*" fraction of 0/1 bits to generalize"*/ ! double gaprob; /*" derived: 1/gafrequency"*/ ! int npool; /*" derived: replacement pool size"*/ ! int nnew; /*" derived: number of new rules"*/ ! int nnulls; /*" unnused bits"*/ ! int *bitlist; /*" dynamic array, length condbits"*/ ! double *problist; /*" dynamic array, length condbits"*/ ! int npoolmax ; /* size of reject array */ int nnewmax ; /* size of newfcast array */ int ncondmax; /* size of newc*/ *************** *** 68,80 **** - init; ! -(int*) getBitListPtr; - - (double *) getProbListPtr; ! - (int *) getMyworldPtr; - -(int)lastgatime; - @end --- 66,81 ---- - init; ! - (int*)getBitListPtr; ! - (void)copyBitList: (int *) p Length: (int) size; ! - (double *)getProbListPtr; ! - (void)copyProbList: (double *) p Length: (int) size; ! - (BFParams *) copy: (id ) aZone; ! ! @end ! diff -cr ASM-20011125/BFParams.m ASM-2.2/BFParams.m *** ASM-20011125/BFParams.m Wed Oct 24 18:26:08 2001 --- ASM-2.2/BFParams.m Tue Nov 6 01:29:38 2001 *************** *** 31,36 **** --- 31,37 ---- {NULL, NOTFOUND} }; + int ReadBitname(const char *variable, const struct keytable *table); id *************** *** 63,123 **** @implementation BFParams ! -(int)lastgatime ! { ! return lastgatime; ! } ! ! - init; { int i; int bits[MAXCONDBITS]; // double probs[MAXCONDBITS]; ! ! bitlist= [ [self getZone] allocBlock: condbits * sizeof(int) ]; ! problist=[ [self getZone] allocBlock: condbits * sizeof(double) ]; - bits[0] = ReadBitname("pr/d>1/4", specialbits); - bits[1] = ReadBitname("pr/d>1/2", specialbits); - bits[2] = ReadBitname("pr/d>3/4", specialbits); - bits[3] = ReadBitname("pr/d>7/8", specialbits); - bits[4] = ReadBitname("pr/d>1", specialbits); - bits[5] = ReadBitname("pr/d>9/8", specialbits); - bits[6] = ReadBitname("pr/d>5/4", specialbits); - bits[7] = ReadBitname("pr/d>3/2", specialbits); - bits[8] = ReadBitname("pr/d>2", specialbits); - bits[9] = ReadBitname("pr/d>4", specialbits); - bits[10] = ReadBitname("p>p5", specialbits); - bits[11] = ReadBitname("p>p20", specialbits); - bits[12] = ReadBitname("p>p100", specialbits); - bits[13] = ReadBitname("p>p500", specialbits); - bits[14] = ReadBitname("on", specialbits); - bits[15] = ReadBitname("off", specialbits); ! for (i=0; i < condbits; i++) { bitlist[i] = bits[i]; //params->problist[i] = probs[i]; problist[i] = bitprob; } - ! // Allocate space for our world bits, clear initially ! condwords = (condbits+15)/16; ! myworld = [[self getZone] allocBlock: condwords* sizeof(unsigned int)]; ! for (i=0; i< condwords; i++) ! myworld[i] = 0; ! // Check bitcost isn't too negative if (1.0+bitcost*(condbits-nnulls) <= 0.0) printf("The bitcost is too negative."); ! // Compute derived parameters gaprob = 1.0/(double)gafrequency; a_range = a_max - a_min; b_range = b_max - b_min; --- 64,199 ---- @implementation BFParams ! /*"BFParams is a class that holds parameter values that might be ! needed by several classes, principally, BFagent, BFCast, or BitVector. ! This class is currently designed so that, if one wants the values of ! the variables here to be individualized, then each agent can be ! created with its own instance of BFParams. A lot of the really ! complicated stuff that used to be in BFagent is now divided between ! this class and BitVector. ! ! This particular version of BFParams has the forecast monitoring up to ! 16 bits of information. In the init method, the names of which 16 ! bits might be monitored are listed explicitly by name. Then the World ! object is asked for the bit number of each name, one-by-one. That ! sequential process fills up the array bitlist. As a result, it is a ! bit difficult to maintain this code and I wish there were an easy way ! out of this. But I didn't find it yet (2001-11-01) ! ! It got to be tedious and boring to maintain getX methods, one for each ! instance variable, so if other classes want values out of this class, ! they can use either of 2 routes. Both are used in BFagent.m, just to ! keep up the variety. One way is the standard Objective-C method to ! obtain instance variable values, the -> operator. Look for usages like ! "privateParams->lambda". The other is a more Swarm oriented probe ! mechanism. Notice the functions at the top of the file BFParams.m, ! like getInt() and getDouble(). To get the lambda parameter, one can say ! getDouble(privateParams,"lambda"). Either of these works, and it ! frees us from the need to constantly add and edit get methods when we ! add or change instance variables in here. ! "*/ ! ! ! /*"Init does an awful lot of work for the BFParam object. It takes ! note of the number of condition bits that can be used and allocates ! space. It also uses a special function ReadBitname to access the ! World object to find out which bit in the World represents which ! piece of information. ! ! Following ASM-2.0, this version of BFParams has condbits set equal ! to 16 bits. In the World, all possible bits are maintained, and one ! can ask for an attribute of the market history by a descriptive name ! like 'pr/d>1/4' and get back the integer value indicating which bit ! in the world keeps that information. The integer keys for the ! moitored bits then get translated into the forecast's instance ! variable bitlist, an an array of integers. Whenever the BFagent ! needs to make sure than a forecast is up to date, it takes that ! bitlist and checks the conditions against the state of the world for ! the monitored bits. ! ! Again following ASM-2.0, we have here forecasts that only use a ! relatively small part of the world, 16 bits. These particular BFCasts ! monitor 10 bits which measure the size of price*interest/dividend, ! 4 more indicators of the change in moving averages of prices for ! various widths of the moving average, and two "dummy" bits fill out ! the array. ! ! It is possible to revise this method to allow monitoring of more ! bits. To add more bits, it is necessary to change the condbits ! instance variable and then write out the names of some variables to ! be monitored inside this init method. As long as the number ! condbits is correct, then the init method should recalculate the ! amount of storage required. In future revisions of ASM, a cleanup ! and revision of this design should be a top priority. ! ! Another issue to consider is the assumption that all forecasts used ! by an agent will use a subset of a given set of bits from the world. ! Probably it would be better to put a bitlist into each forecast, and ! completely de-couple the forecasts. ! "*/ - init; { int i; int bits[MAXCONDBITS]; // double probs[MAXCONDBITS]; ! //pj 2001-11-02. For ASM-2.2, I'm sticking with the ASM-2.0 ! //"all agents have 16 bits" rule. But I'm not sticking with it ! //after that! With USEALLBITS, I'm ! //just experimenting to see what difference it makes. ! // ! BOOL USEALLBITS = NO; ! ! if (USEALLBITS!=YES) ! { ! bits[0] = ReadBitname("pr/d>1/4", specialbits); ! bits[1] = ReadBitname("pr/d>1/2", specialbits); ! bits[2] = ReadBitname("pr/d>3/4", specialbits); ! bits[3] = ReadBitname("pr/d>7/8", specialbits); ! bits[4] = ReadBitname("pr/d>1", specialbits); ! bits[5] = ReadBitname("pr/d>9/8", specialbits); ! bits[6] = ReadBitname("pr/d>5/4", specialbits); ! bits[7] = ReadBitname("pr/d>3/2", specialbits); ! bits[8] = ReadBitname("pr/d>2", specialbits); ! bits[9] = ReadBitname("pr/d>4", specialbits); ! bits[10] = ReadBitname("p>p5", specialbits); ! bits[11] = ReadBitname("p>p20", specialbits); ! bits[12] = ReadBitname("p>p100", specialbits); ! bits[13] = ReadBitname("p>p500", specialbits); ! bits[14] = ReadBitname("on", specialbits); ! bits[15] = ReadBitname("off", specialbits); ! } ! else ! { ! condbits = 60; ! ! for(i=0; i < condbits; i++) bits[i]=i; ! } ! bitlist= [ [self getZone] alloc: condbits * sizeof(int) ]; ! problist=[ [self getZone] alloc: condbits * sizeof(double) ]; ! for (i=0; i < condbits; i++) { bitlist[i] = bits[i]; //params->problist[i] = probs[i]; problist[i] = bitprob; } ! // Allocate space for our world bits, clear initially ! condwords = (condbits+15)/16; ! // myworld = [[self getZone] allocBlock: condwords* sizeof(unsigned int)]; ! // for (i=0; i< condwords; i++) ! // myworld[i] = 0; ! // Check bitcost isn't too negative if (1.0+bitcost*(condbits-nnulls) <= 0.0) printf("The bitcost is too negative."); ! // Compute derived parameters gaprob = 1.0/(double)gafrequency; a_range = a_max - a_min; b_range = b_max - b_min; *************** *** 126,132 **** npool = (int)(numfcasts*poolfrac + 0.5); nnew = (int)(numfcasts*newfrac + 0.5); ! // Record maxima needed for GA working space if (npool > npoolmax) npoolmax = npool; if (nnew > nnewmax) nnewmax = nnew; if (condwords > ncondmax) ncondmax = condwords; --- 202,208 ---- npool = (int)(numfcasts*poolfrac + 0.5); nnew = (int)(numfcasts*newfrac + 0.5); ! // Record maxima needed for GA working space if (npool > npoolmax) npoolmax = npool; if (nnew > nnewmax) nnewmax = nnew; if (condwords > ncondmax) ncondmax = condwords; *************** *** 134,152 **** return [super createEnd]; } ! -(int*) getBitListPtr { return bitlist; } ! - (double *) getProbListPtr { return problist; } ! - (int *) getMyworldPtr { ! return myworld; } --- 210,249 ---- return [super createEnd]; } ! - (int*)getBitListPtr { return bitlist; } ! /*"if passed a pointer to an array of integers of length size, this ! frees the old bitlist and puts the new one in its place"*/ ! - (void)copyBitList: (int *)x Length: (int) size ! { ! int i; ! if (bitlist) [[self getZone] free: bitlist]; ! bitlist = [[self getZone] alloc: size * sizeof(int) ]; ! for (i=0; i < size; i++) ! { ! bitlist[i] = x[i]; ! } ! } ! ! - (double *)getProbListPtr { return problist; } ! /*"if passed a pointer to a double with a given size, this frees the ! old bitlist and puts the new one in its place"*/ ! - (void)copyProbList: (double *) p Length: (int) size { ! int i; ! if (problist) [[self getZone] free: problist]; ! problist = [[self getZone] alloc: size * sizeof(double) ]; ! for (i=0; i < size; i++) ! { ! problist[i] = p[i]; ! } } *************** *** 174,179 **** --- 271,341 ---- } + /*"Create a copy of this BFParams instance. Note this copies EVERY + instance variable, one by one"*/ + - (BFParams *) copy: (id ) aZone + { + BFParams * bfParams; + + //Why not begin with a totally fresh instance created from scratch, + //The way your old granny used to do it? + if ((bfParams = + [lispAppArchiver getWithZone: aZone key: "bfParams"]) == nil) + raiseEvent(InvalidOperation, + "Can't find the BFParam's parameters"); + + //Then replace all those values granny liked (: + + bfParams->numfcasts = numfcasts; + bfParams-> condwords = condwords ; + bfParams->condbits = condbits; + bfParams->mincount = mincount; + bfParams->gafrequency = gafrequency; + bfParams->firstgatime = firstgatime; + bfParams->longtime = longtime; + bfParams->individual = individual; + bfParams->tauv = tauv; + bfParams->lambda = lambda; + bfParams-> maxbid = maxbid; + bfParams->bitprob = bitprob; + bfParams-> subrange = subrange; + bfParams-> a_min = a_min; + bfParams->a_max = a_max; + bfParams-> b_min = b_min; + bfParams->b_max = b_max; + bfParams->c_min = c_min; + bfParams->c_max = c_max; + bfParams->a_range = a_range; + bfParams->b_range = b_range; + bfParams->c_range = c_range; + bfParams->newfcastvar = newfcastvar; + bfParams->initvar = initvar; + bfParams->bitcost = bitcost; + bfParams->maxdev = maxdev; + bfParams->poolfrac = poolfrac; + bfParams->newfrac = newfrac; + bfParams->pcrossover = pcrossover; + bfParams->plinear = plinear; + bfParams->prandom = prandom; + bfParams->pmutation = pmutation; + bfParams->plong = plong; + bfParams->pshort = pshort; + bfParams->nhood = nhood; + bfParams->genfrac = genfrac; + bfParams->gaprob = gaprob; + bfParams->npool = npool; + bfParams->nnew = nnew; + bfParams->nnulls = nnulls; + bfParams->npoolmax = npoolmax; + bfParams->nnewmax = nnewmax; + bfParams->ncondmax = ncondmax; + + [bfParams copyBitList: bitlist Length: condbits]; + [bfParams copyProbList: problist Length: condbits]; + return bfParams; + } + + @end diff -cr ASM-20011125/BFagent.h ASM-2.2/BFagent.h *** ASM-20011125/BFagent.h Fri Jun 2 13:01:21 2000 --- ASM-2.2/BFagent.h Thu Nov 1 21:42:12 2001 *************** *** 16,87 **** @interface BFagent:Agent { ! int currentTime; ! int lastgatime; // last time a GA was run ! double avspecificity; ! double forecast; ! double lforecast; ! double global_mean; ! double realDeviation; ! double variance; ! double pdcoeff; ! double offset; ! double divisor; ! // struct BF_fcast *fcast; // array of size numfcasts ! //struct BF_fcast *activelist; ! //struct BF_fcast *lactivelist; // last active list ! // struct BFparams *p; ! int gacount; ! int nactive; ! BFParams * privateParams; //created from same mechanism as public params ! // struct BF_fcast **reject; /* GA temporary storage */ ! // struct BF_fcast *newfcast; /* GA temporary storage */ ! //id rejectList; //need ** accounted for ??? ! id fcastList; ! //id newconds; ! id activeList; ! id oldActiveList; } ! +(void)setBFParameterObject: x; ! +(void)init; ! //+didInitialize; ! //+prepareForTrading; ! //+(int)lastgatime; ! +setRealWorld: (int *)array; ! +(int)setNumWorldBits; ! ! -createEnd; ! -initForecasts; ! ! - (BFCast *) createNewForecast; //all conditions=0 ! ! -setConditionsRandomly: (BFCast *) fcastObject; //apply to forecast ! //-free; ! -prepareForTrading; ! -(BitVector *) collectWorldData: aZone; ! -updateActiveList: (BitVector *) worldvalues; ! ! -getInputValues; //does nothing, used only if their are ANNagents ! -feedForward; //does nothing, used only if their are ANNagents ! -(double)getDemandAndSlope:(double *)slope forPrice:(double)trialprice; ! -(double)getRealForecast; ! -updatePerformance; ! -(double)getDeviation; ! -updateWeights; //does nothing, used only if their are ANNagents ! -(int)nbits; ! -(int)nrules; ! -(int)lastgatime; ! -printcond: (int) word; - copyList: list To: outputList; ! -(int)bitDistribution:(int *(*)[4])countptr cumulative:(BOOL)cum; ! -(int)fMoments: (double *)moment cumulative: (BOOL)cum; ! -(const char *)descriptionOfBit:(int)bit; @end --- 16,75 ---- @interface BFagent:Agent { ! int currentTime; /*"The agent regularly checks with Swarm to see what time it is"*/ ! int lastgatime; /*" last time period when the GeneticAlgorithm was run"*/ ! double avspecificity; /*'average specificity of active forecasts"*/ ! double forecast; /*"prediction of stock price: (trialprice+dividend)*pdcoeff + offset."*/ ! double lforecast; /*"lagged forecast: forecast value from previous period"*/ ! double global_mean; /*"price+dividend"*/ ! double realDeviation; /*" ftarget-lforecast: how far off was the agent's forecast?"*/ ! double variance; /*"an Exp.Weighted MA of the agent's historical variance: Combine the old variance with deviation^squared, as in: bv*variance + av*deviation*deviation"*/ ! double pdcoeff; /*" coefficient used in predicting stock price, recalculated each period in prepareForTrading"*/ ! double offset; /*" coefficient used in predicting stock price, recalculated each period in prepareForTrading"*/ ! double divisor; /*" a coefficient used to calculate demand for stock. It is a proportion (lambda) of forecastvar (basically, accuracy of forecasts)"*/ ! int gacount; /*" how many times has the Genetic Algorithm been used?"*/ ! // int nactive; ! BFParams * privateParams; /*"BFParams object holds parameters of this object"*/ ! id fcastList; /*"A Swarm Array, holding the forecasts that the agent might use"*/ ! id activeList; /*"A Swarm list containing a subset of all forecasts"*/ ! id oldActiveList; /*"A copy of the activeList from the previous time step"*/ } ! + (void)setBFParameterObject: x; ! + (void)init; ! - createEnd; ! - initForecasts; ! ! - (BFCast *)createNewForecast; //all conditions=0 ! ! - setConditionsRandomly: (BFCast *)fcastObject; //apply to forecast ! - prepareForTrading; ! - (BitVector *) collectWorldData: aZone; ! - updateActiveList: (BitVector *)worldvalues; ! ! - getInputValues; //does nothing, used only if their are ANNagents ! - feedForward; //does nothing, used only if their are ANNagents ! - (double)getDemandAndSlope: (double *)slope forPrice: (double)trialprice; ! - (double)getRealForecast; ! - updatePerformance; ! - (double)getDeviation; ! - updateWeights; //does nothing, used only if their are ANNagents ! - (int)nbits; ! - (int)nrules; ! ! - performGA; ! - (int)lastgatime; ! ! - printcond: (int)word; - copyList: list To: outputList; ! - (int)bitDistribution:(int *(*)[4])countptr cumulative:(BOOL)cum; ! - (int)fMoments: (double *)moment cumulative: (BOOL)cum; ! - (const char *)descriptionOfBit:(int)bit; @end diff -cr ASM-20011125/BFagent.m ASM-2.2/BFagent.m *** ASM-20011125/BFagent.m Thu Oct 25 16:41:40 2001 --- ASM-2.2/BFagent.m Tue Nov 6 01:29:38 2001 *************** *** 1,61 **** // Code for a "bitstring forecaster" (BF) agent - // +init - // Initializes the class, setting parameters and allocating space - // for arrays. - // - // +didInitialize - // Tells the agent class object that initialization (including creation - // of agents) is finished. - // - // +prepareForTrading //deleted by pj May 1, 2000 - - // +(BOOL)lastgatime - // Returns the most recent time at which a GA ran for any agent of - // this type. - // - // -free //now handled by "drop" method in "BFCast.m" - // - // -(int *(*)[4])bitDistribution; - // Returns a pointer to an array of 4 pointers to arrays containing the - // number of 00's, 01's, 10's, and 11's for each of this agent's - // condition bits, summed over all rules/forecasters. Agents that - // don't use condition bits return NULL. This uses the method - // -bitDistribution:cumulative: described below that is provided by - // subclasses that have condition bits. - // - // -(int)nbits - // Returns the number of condition bits used by this agent, or 0 if - // condition bits aren't used. - // - // -(const char *)descriptionOfBit: (int)bit - // If the agent uses condition bits, returns a description of the - // specified bit. Invalid bit numbers return an explanatory message. - // Agents that don't use condition bits return NULL. - // - // -(int)nrules - // Returns the number of rules or forecasters used by this agent, or 0 - // if rules/forecasters aren't used. - // - // -(int)lastgatime - // Returns the last time at which an agent's genetic algorithm was run. - // Agents that don't use a genetic algorithm return MININT. This may - // be used to see if the bit distribution might have changed, since - // a change can only occur through a genetic algorithm. - // - // -(int)bitDistribution:(int *(*)[4])countptr cumulative: (BOOL)cum - // Places in (*countptr)[0] -- (*countptr)[3] the addresses of 4 - // arrays, (*countptr)[0][i] -- (*countptr)[3][i], which are filled - // with the number of bits that are 00, 01, 10, or 11 respectively, - // for each condition bit i= 0, 1, nbits-1, summed over all rules or - // forecasters. Returns nbits, the number of condition bits. If - // cum is YES, adds the new counts to whatever is in the (*coutptr) - // arrays already. Agents that don't use condition bits return -1. - // The 4-element array (*countptr)[4] must already exist, but the - // arrays to which its element point are supplied dynamically. This - // method must be provided by each subclass that has condition bits. - // /* pj: change comments June 2, 2000 I began with the code as released by the ASM research team through --- 1,20 ---- // Code for a "bitstring forecaster" (BF) agent /* + pj: change comments Nov. 2, 2001. + + In 2000 comments, I said I was leaving the privateParams object as + a shared thing used by all BFagents. Well, I just could not stand + that anymore and so now I have the global BFParams object, the one + for which the GUI shows and it is the default for all instances, + but now each BFagent gets its own instance of BFParams and it is + a copy of BFParams, at least to start. That object privateParams + can now be individualized for each agent. In particular, one thing + on the TODO list has to be the individualization of bitlists. + + Other changes described from last year are all still fine. + + pj: change comments June 2, 2000 I began with the code as released by the ASM research team through *************** *** 237,257 **** #define drand() [uniformDblRand getDoubleWithMin: 0 withMax: 1] #define urand() [uniformDblRand getDoubleWithMin: -1 withMax: 1] #define irand(x) [uniformIntRand getIntegerWithMin: 0 withMax: x-1] - - //Macros for bit manipulation - //pj: Sometimes I use these for diagnostic checking. - // #define WORD(bit) (bit>>4) - // #define MAXCONDBITS 80 - // #define extractvalue(variable, trit) ((variable[WORD(trit)] >> ((trit%16)*2))&3) - // #define ifnilgetzero(variable, trit) ((variable[WORD(trit)]& (3<<((trit%16)*2)))) - ! // Type of forecasting. WEIGHTED forecasting is untested in its present form. ! //pj: bluntly, WEIGHTED does not work and is incomplete #define WEIGHTED 0 - //static void makebittables(void); //now in BFCast.m - //pj: this is a static global declaration of the params object, shared by all instances. //pj: note there is also a local copy which is, in current code, intitially the same thing, //pj: and it never changes. The original code had 3 of these, so I'm slimmer by 1/3. --- 196,208 ---- #define drand() [uniformDblRand getDoubleWithMin: 0 withMax: 1] #define urand() [uniformDblRand getDoubleWithMin: -1 withMax: 1] #define irand(x) [uniformIntRand getIntegerWithMin: 0 withMax: x-1] ! // Type of forecasting. WEIGHTED forecasting is untested in its ! // present form. ! //pj: bluntly, WEIGHTED does not work and is incomplete, It never worked ! // in ASM-2.0, and that's why it is commented out by setting WEIGHTED to 0. #define WEIGHTED 0 //pj: this is a static global declaration of the params object, shared by all instances. //pj: note there is also a local copy which is, in current code, intitially the same thing, //pj: and it never changes. The original code had 3 of these, so I'm slimmer by 1/3. *************** *** 263,278 **** //pj: ReadBitname moved to BFParams //pj: This is the only global variable I still need, and I'm looking for a way go get rid of it! ! static double minstrength; // PRIVATE METHODS @interface BFagent(Private) - - performGA; - //pj: methods now replace previous functions: - (BFCast *) CopyRule:(BFCast *) to From: (BFCast *) from; ! - (void) MakePool: rejects From: (id ) list; - (BOOL) Mutate: (BFCast *) new Status: (BOOL) changed; - (BFCast *) Crossover:(BFCast *) newForecast Parent1: (BFCast *) parent1 Parent2: (BFCast *) parent2; - (void) TransferFcastsFrom: newList To: forecastList Replace: rejects; --- 214,227 ---- //pj: ReadBitname moved to BFParams //pj: This is the only global variable I still need, and I'm looking for a way go get rid of it! ! static double minstrength; // PRIVATE METHODS @interface BFagent(Private) //pj: methods now replace previous functions: - (BFCast *) CopyRule:(BFCast *) to From: (BFCast *) from; ! - (void) MakePool: (id )rejects From: (id ) list; - (BOOL) Mutate: (BFCast *) new Status: (BOOL) changed; - (BFCast *) Crossover:(BFCast *) newForecast Parent1: (BFCast *) parent1 Parent2: (BFCast *) parent2; - (void) TransferFcastsFrom: newList To: forecastList Replace: rejects; *************** *** 284,348 **** @implementation BFagent ! ! +(void) setBFParameterObject: x { params=x; } ! +(void) init { ! [BFCast init]; //must pass along init statement to BFCast,then to ! //BitVector from there. ! return; } ! //pj: none of this functionality is needed anymore ! // +didInitialize ! // +prepareForTrading //called at the start of each trading period ! ! //Don't need a class method here for this. If you need something like ! //it, put it in BFParams. ! //+(int)lastgatime ! // { pp = (struct BFparams *)params; ! // return pp->lastgatime; ! // } ! ! ! //pj: yikes, pointer usage, be careful. ! +setRealWorld: (int *)array { - [worldForAgent getRealWorld: array]; - return self; - } - - //pj: superfluous method: never called anymore - +(int)setNumWorldBits - { - int numofbits; - numofbits = [worldForAgent getNumWorldBits]; - return numofbits; - } - - -createEnd - { - - //pj: container objects activeList=[List create: [self getZone]]; oldActiveList=[List create: [self getZone]]; - - - return [super createEnd]; } ! ! -initForecasts { int sumspecificity = 0; - - //pj:new vars int i; BFCast * aForecast; int numfcasts; --- 233,315 ---- @implementation BFagent ! /*"The BFagent--"bitstring forecasting agent" is the centerpiece of ! the ASM model. The agent competes in a stock market, it buy, it ! sells. It decides to buy or sell by making predictions about what the ! price of the stock is likely to do in future. In order to make ! predictions, it keeps a large list of forecast objects on hand, and ! each forecast object makes a price prediction. These forecasts, which ! are created from the BFCast subclass, are fairly sophisticated ! entities, they may monitor many different conditions of the world. ! The forecast which has the best performance record at any given ! instant is used to predict the future price, which in turn leads to ! the buy/sell decision. ! ! Inside the file BFagent.m, there is a long set of comments about the ! updating that went on in the redesign of this code for ASM-2.2. In ! order to faciliate this revision, several new classes were introduced. ! BFParams is an object that keeps values of the parameters for ! BFagents, and BFCast is the forecast object itself. BFCast, in turn, ! keeps its conditions bits with a subclass called BitVector. ! ! If you dig into the code of this agent, you will find a confusing ! thing, so be warned. This code and articles based on it use the term ! "bit" to refer to something that can be valued either 0, 1, or 2. 0 ! means "don't care," 1 means "NO" and 2 means "YES". The confusing ! thing is that it takes two bits to represent this amount of ! information. In binary, the values would be {00,01,10}, ! respectively. I'm told some people call these trits to keep that in ! mind that two digits are required. As a result of the fact that it ! takes "two bits" to store "one bit's" worth of information, some ! relatively complicated book keeping has to be done. That's where all ! the parameters like "condbits" and "condwors" come into play. In ! ASM-2.0, that book keeping was all "manually done" right here in ! BFagent.m, but in the 2.2 version, it is all hidden in the subclass ! BitVector. So, for purposes of the interface of this class, a bit is ! a 3 valued piece of information, and values of bits inside forecasts ! are set by messages to the forecast, like [aForecast setConditionsbit: ! bit FromZeroTo: 2], for example, will set that bit to 2. If you want ! to know if a forecast has YES or NO for a bit x, [aForecast ! getConditionsbit: x]. "*/ ! ! ! /*"This tells BFagents where they should look to get the default ! parameters. it should give the agent an object from the BFParams ! class."*/ ! + (void)setBFParameterObject: x { params=x; } ! /*"This is vital to set values in the forecast class, BFCast, which in ! turn initializes BitVector class"*/ ! + (void)init { ! [BFCast init]; return; } ! /*"This creates the container objects activeList and oldActiveList. ! In addition, it makes sure that any initialization in the createEnd ! of the super class is done."*/ ! - createEnd { activeList=[List create: [self getZone]]; oldActiveList=[List create: [self getZone]]; return [super createEnd]; } ! /*"initForecasts. Creates BFCast objects (forecasts) and puts them ! into an array called fCastList. These are the "meat" of this ! agent's functionality, as they are repeatedly updated, improved, and ! tested in the remainder of the class. Please note each BFagent has ! a copy of the default params object called privateParams. It can be ! used to set individualized values of settings in BFParams for each ! agent. That would allow true diversity! I don't see how that diversity ! would be allowed for in the ASM-2.0."*/ ! - initForecasts { int sumspecificity = 0; int i; BFCast * aForecast; int numfcasts; *************** *** 350,367 **** // Initialize our instance variables ! //pj: in the future, it may be good to have a separate parameter object for each BFagent. ! //pj: now it makes little sense, so I'm commenting out the creation code here and just setting ! //pj: privateParams equal to the global variable that is passed in. ! // if ((privateParams = ! // [lispAppArchiver getWithZone: [self getZone] key: "bfParams"]) == nil) ! // raiseEvent(InvalidOperation, ! // "Can't find the BFParams parameters"); ! // [privateParams init]; ! ! privateParams= params; ! numfcasts=getInt(privateParams,"numfcasts"); fcastList=[Array create: [self getZone] setCount: numfcasts]; --- 317,334 ---- // Initialize our instance variables ! //all instances of BFagent can use the same BFParams object. ! //ASM-2.0 was written that way, something like: ! // privateParams= params; ! ! // That seemed fraught with danger, with all instances having ! // read/write access to a global parameter object, so now I'm ! // creating a copy that each agent can have and individualize. ! privateParams = [params copy: [self getZone]]; ! //If you want to customize privateParams, this is the spot! ! ! numfcasts = getInt(privateParams,"numfcasts"); fcastList=[Array create: [self getZone] setCount: numfcasts]; *************** *** 375,381 **** forecast = lforecast = global_mean; ! // Iniitialize the forecasts, put them into Swarm Array //keep the 0'th forecast in a "know nothing" condition [fcastList atOffset: 0 put: [self createNewForecast]]; --- 342,348 ---- forecast = lforecast = global_mean; ! // Initialize the forecasts, put them into Swarm Array //keep the 0'th forecast in a "know nothing" condition [fcastList atOffset: 0 put: [self createNewForecast]]; *************** *** 385,391 **** { id aForecast =[self createNewForecast] ; [self setConditionsRandomly: aForecast]; ! [fcastList atOffset: i put: aForecast];//pj: put aForecast into Swarm array "fcastlist" } /* Compute average specificity */ --- 352,358 ---- { id aForecast =[self createNewForecast] ; [self setConditionsRandomly: aForecast]; ! [fcastList atOffset: i put: aForecast]; //put aForecast into Swarm array "fcastlist" } /* Compute average specificity */ *************** *** 402,409 **** return self; } ! //pj: creates forecasts with all condition bits are 00 here, "don't care" ! - (BFCast *) createNewForecast { BFCast * aForecast; //needed to set values of a,b,and c --- 369,380 ---- return self; } ! /*"Creates a new forecast object (instance of BFCast), with all ! condition bits set to 00 here, meaning "don't care. It also sets ! values for the other coefficients inside the BFCast. This method is ! accessed at several points throughout the BFagent class when new ! forecasts are needed."*/ ! - (BFCast *)createNewForecast { BFCast * aForecast; //needed to set values of a,b,and c *************** *** 446,453 **** return aForecast; } ! ! -setConditionsRandomly: (BFCast *) fcastObject { int bit; double *problist; --- 417,428 ---- return aForecast; } ! /*"Take a forecast object and randomly change the bits that govern ! which conditions it monitors. This appears to be a piece of ! functionality that could move to the BFCast class itself. There were ! quite a few of these details floating around in BFagent at one time, ! many are gone now."*/ ! - setConditionsRandomly: (BFCast *)fcastObject { int bit; double *problist; *************** *** 466,473 **** else if (drand() < problist[bit]) { [fcastObject setConditionsbit: bit FromZeroTo: irand(2)+1]; ! //remember 1 means yes, or binary 01, and 2 means no, or 10 ! [fcastObject incrSpecificity];//I wish this were automatic! [fcastObject updateSpecfactor]; } } --- 441,448 ---- else if (drand() < problist[bit]) { [fcastObject setConditionsbit: bit FromZeroTo: irand(2)+1]; ! //remember 1 means no, or binary 01, and 2 means Yes, or 10 ! [fcastObject incrSpecificity];//pj: I wish this were automatic! [fcastObject updateSpecfactor]; } } *************** *** 475,494 **** } ! -prepareForTrading ! /* * Set up a new active list for this agent's forecasts, and compute the * coefficients pdcoeff and offset in the equation * forecast = pdcoeff*(trialprice+dividend) + offset * * The active list of all the fcasts matching the present conditions is saved * for later updates. ! */ { //register struct BF_fcast *fptr, *topfptr, **nextptr; //unsigned int real0, real1, real2, real3, real4 = 0 ; double weight, countsum, forecastvar=0.0; int mincount; //pj: for getting values from world BitVector * myworld; --- 450,470 ---- } ! - prepareForTrading ! /*" * Set up a new active list for this agent's forecasts, and compute the * coefficients pdcoeff and offset in the equation * forecast = pdcoeff*(trialprice+dividend) + offset * * The active list of all the fcasts matching the present conditions is saved * for later updates. ! "*/ { //register struct BF_fcast *fptr, *topfptr, **nextptr; //unsigned int real0, real1, real2, real3, real4 = 0 ; double weight, countsum, forecastvar=0.0; int mincount; + int nactive; //pj: for getting values from world BitVector * myworld; *************** *** 532,538 **** //pj: note I started updating this code to match the rest, but then //pj: I realized it did not work as it was before, so I stopped ! //pj: messing with it. a = 0.0; b = 0.0; --- 508,515 ---- //pj: note I started updating this code to match the rest, but then //pj: I realized it did not work as it was before, so I stopped ! //pj: messing with it. Don't expect the CPPFLAG WEIGHTED to do ! //pj: anything good. a = 0.0; b = 0.0; *************** *** 570,583 **** } #else //NOT WEIGHTED MODEL ! // Now go through the list and find best forecast maxstrength = -1e50; - //bestfptf=NULL bestForecast = nil; nactive = 0; mincount = getInt(privateParams,"mincount"); ! //pj: Kept as example of "homemade list" // for (fptr=activelist; fptr!=NULL; fptr=fptr->next) // { // fptr->lastactive = currentTime; --- 547,559 ---- } #else //NOT WEIGHTED MODEL ! // Go through the list and find best forecast maxstrength = -1e50; bestForecast = nil; nactive = 0; mincount = getInt(privateParams,"mincount"); ! //pj: Kept as example of "homemade list" in ASM-2.0 // for (fptr=activelist; fptr!=NULL; fptr=fptr->next) // { // fptr->lastactive = currentTime; *************** *** 592,597 **** --- 568,577 ---- // } // } + + //??Following code causes a bug when numfcasts is small. It causes + //nactive >0 even though there is no best forecast. ?? Track it down + //This problem existed in ASM-2.0, should back track it. index=[activeList begin: [self getZone]]; for( aForecast=[index next]; [index getLoc]==Member; aForecast=[index next] ) { *************** *** 609,628 **** } [index drop]; ! // if (nactive) // { // pdcoeff = bestfptr->a; // offset = bestfptr->b*dividend + bestfptr->c; // forecastvar = (privateParams->individual? bestfptr->variance :variance); // } ! ! //fprintf(stderr,"nactive = %d pjactive %d activeList getCount is %d\n",nactive,pjactivecount, [activeList getCount]); if (nactive) // meaning that at least some forecasts are active { pdcoeff = [bestForecast getAval]; offset = [bestForecast getBval]*dividend + [bestForecast getCval]; ! forecastvar = getInt(privateParams,"individual")? [bestForecast getVariance]:variance; } #endif --- 589,607 ---- } [index drop]; ! // Here is the way it was in ASM-2.0 // if (nactive) // { // pdcoeff = bestfptr->a; // offset = bestfptr->b*dividend + bestfptr->c; // forecastvar = (privateParams->individual? bestfptr->variance :variance); // } ! if (nactive) // meaning that at least some forecasts are active { pdcoeff = [bestForecast getAval]; offset = [bestForecast getBval]*dividend + [bestForecast getCval]; ! forecastvar = getInt(privateParams,"individual")? [bestForecast getVariance]: variance; } #endif *************** *** 636,643 **** mincount = getInt(privateParams,"mincount"); ! index=[ fcastList begin: [self getZone]]; ! for( aForecast=[index next]; [index getLoc]==Member; aForecast=[index next] ) { if ([aForecast getCnt] >= mincount) { --- 615,622 ---- mincount = getInt(privateParams,"mincount"); ! index = [fcastList begin: [self getZone]]; ! for ( aForecast=[index next]; [index getLoc]==Member; aForecast=[index next] ) { if ([aForecast getCnt] >= mincount) { *************** *** 665,672 **** return self; } ! ! -(BitVector *) collectWorldData: aZone; { int i,n,nworldbits; BitVector * world; --- 644,658 ---- return self; } ! /*"A forecast has a set of conditions it is watching. These are packed ! tight in a BitVector. We need the world data about the status of those ! conditions packed the same way, in order to make quick checks to find ! out if the world conditions are matched by the BitVector's ! conditions. This method creates a BitVector to match the conditions ! that are being monitored by the agent's forecasts. This requires the ! use of the design assumption that all of an agent's forecasts have the ! same bitlist."*/ ! - (BitVector *)collectWorldData: aZone; { int i,n,nworldbits; BitVector * world; *************** *** 699,712 **** } ! //pj: here is the comment from BFagent about the next thing. ! // Main inner loop over forecasters. We set this up separately for each ! // value of condwords, for speed. It's ugly, but fast. Don't mess with ! // it! Taking out fptr->conditions will NOT make it faster! The highest ! // condwords allowed for here sets the maximum number of condition bits ! // permitted (no matter how large MAXCONDBITS). ! ! -updateActiveList: (BitVector *) worldvalues { id index; BFCast * aForecast; --- 685,700 ---- } ! /*"This is the main inner loop over forecasts. Go through the list ! of active forecasts, compare how they did against the world. Notice ! the switch that checks to see how big the bitvector (condwords) is ! before proceeding. At one time, this gave a significant ! speedup. The original sfsm authors say 'Its ugly, but it ! works. Don't mess with it!' (pj: I've messed with it, and don't ! notice much of a speed effect on modern computers with modern ! compilers :> My alternative implementation is commented out inside ! this method)"*/ ! - updateActiveList: (BitVector *)worldvalues { id index; BFCast * aForecast; *************** *** 743,749 **** break; case 2: ! //pj: here is how it used to be // real1 = worldvalues[1]; // for (fptr = fcast; fptr < topfptr; fptr++) --- 731,737 ---- break; case 2: ! //pj: here is how it used to be in ASM-2.0 // real1 = worldvalues[1]; // for (fptr = fcast; fptr < topfptr; fptr++) *************** *** 815,822 **** //pj??? There ought to be a "default" action here for other cases. ! /*This is an alternative implementation of the same as preceeding. Its so much cuter. ! I write it before I understood the fact that the World gives back 10 for yes and agent has 01 for yes. index=[ fcastList begin: [self getZone]]; for ( aForecast=[index next]; [index getLoc]==Member; aForecast=[index next] ) --- 803,813 ---- //pj??? There ought to be a "default" action here for other cases. ! /*This is an alternative implementation of the same as preceeding. ! It is so much cuter in my view. I wrote it before I understood the ! fact that the World gives back 10 for yes and agent has 01 for yes, so ! you have to be careful with it. Note the bitmath here, that appears ! as it used to throughout the BFagent class. index=[ fcastList begin: [self getZone]]; for ( aForecast=[index next]; [index getLoc]==Member; aForecast=[index next] ) *************** *** 845,885 **** if ( flag!=1 ) [activeList addLast: aForecast]; } ! [index drop]; ! */ return self; } ! ! -getInputValues //does nothing, used only if their are ANNagents { return self; } ! ! -feedForward //does nothing, used only if their are ANNagents { return self; } ! -(double)getDemandAndSlope: (double *)slope forPrice: (double)trialprice ! /* ! * Returns the agent's requested bid (if >0) or offer (if <0) using ! * best (or mean) linear forecast chosen by -prepareForTrading ! */ ! { ! // The actual forecast is given by ! // forecast = pdcoeff*(trialprice+dividend) + offset ! // where pdcoeff and offset are set by -prepareForTrading. forecast = (trialprice + dividend)*pdcoeff + offset; ! // A risk aversion computation now gives a target holding, and its ! // derivative ("slope") with respect to price. The slope is calculated ! // as the linear approximated response of a change in price on the traders' ! // demand at time t, based on the change in the forecast according to the ! // currently active linear rule. if (forecast >= 0.0) { demand = -((trialprice*intratep1 - forecast)/divisor + position); --- 836,878 ---- if ( flag!=1 ) [activeList addLast: aForecast]; } ! [index drop]; */ return self; } ! /*"Currently does nothing, used only if their are ANNagents"*/ ! - getInputValues { return self; } ! /*"Currently does nothing, used only if their are ANNagents"*/ ! - feedForward { return self; } ! - (double)getDemandAndSlope: (double *)slope forPrice: (double)trialprice ! /*" Returns the agent's requested bid (if >0) or offer (if <0) using ! * best (or mean) linear forecast chosen by -prepareForTrading. The ! * forecast is given by ! ! forecast = pdcoeff*(trialprice+dividend) + offset ! ! * where pdcoeff and offset are set by -prepareForTrading. ! ! A risk aversion computation gives a target holding, and its ! derivative ("slope") with respect to price. The slope is calculated ! as the linear approximated response of a change in price on the ! traders' demand at time t, based on the change in the forecast ! according to the currently active linear rule. "*/ ! ! { forecast = (trialprice + dividend)*pdcoeff + offset; ! if (forecast >= 0.0) { demand = -((trialprice*intratep1 - forecast)/divisor + position); *************** *** 911,931 **** } ! -(double)getRealForecast { return forecast; } ! -updatePerformance { //pj: register struct BF_fcast *fptr; BFCast * aForecast; id index = nil; double deviation, ftarget, tauv, a, b, c, av, bv, maxdev; - - // Now update all the forecasts that were active in the previous period, - // since now we know how they performed. // Precompute things for speed tauv = privateParams->tauv; --- 904,936 ---- } ! /*"Return agent's forecast"*/ ! - (double)getRealForecast { return forecast; } ! /*" Now update the variance and strength of all the forecasts that ! were active in the previous period, since now we know how they ! performed. This method causes an update of price/dividend ! information from the world, then it measures how far off each ! forecast was and puts the square of that "deviance" measure into the ! forecast with the forecast's setVariance: method. Each forecast in ! the active list is told to update its forecast. It also updates the ! instance variable variance, which is calculated here as an ! exponentially weignted moving average of that forecast's ! squared-error (variance). Inside the code of updatePerformance, ! there is a description of the strength formula that is used, and how ! the formula now matches the formula used in the original sfsm, ! rather than ASM-2.0. "*/ ! ! - updatePerformance { //pj: register struct BF_fcast *fptr; BFCast * aForecast; id index = nil; double deviation, ftarget, tauv, a, b, c, av, bv, maxdev; // Precompute things for speed tauv = privateParams->tauv; *************** *** 1008,1015 **** index = [ oldActiveList begin: [self getZone]]; for( aForecast=[index next]; [index getLoc]==Member; aForecast=[index next] ) { ! double lastForecast; ! lastForecast=[aForecast getLforecast]; deviation = (ftarget - lastForecast)*(ftarget - lastForecast); if (deviation > maxdev) deviation = maxdev; --- 1013,1019 ---- index = [ oldActiveList begin: [self getZone]]; for( aForecast=[index next]; [index getLoc]==Member; aForecast=[index next] ) { ! double lastForecast=[aForecast getLforecast]; deviation = (ftarget - lastForecast)*(ftarget - lastForecast); if (deviation > maxdev) deviation = maxdev; *************** *** 1021,1073 **** [aForecast setVariance: (1.0 - c)*[aForecast getVariance] + c*deviation]; } ! [aForecast setStrength: privateParams->maxdev - [aForecast getVariance] + [aForecast getSpecfactor]]; //based on bfagent.m ! // original bfagent has this: ! // rptr->strength = p->maxdev - rptr->variance + rptr->specfactor; ! // BFagent had this: fptr->strength = fptr->specfactor/fptr->variance; } [index drop]; } ! // NOTE: On exit, fptr->forecast is only guaranteed to be valid for ! // forcasters which matched. The inspector has to calculate the rest ! // itself if it wants to show them all. This is for speed. return self; } ! ! -(double)getDeviation { return fabs(realDeviation); } ! ! -updateWeights //does nothing, used only if their are ANNagents { return self; } ! -(int)nbits { return privateParams->condbits; } ! ! -(int)nrules { return privateParams->numfcasts; } ! ! -(int)lastgatime { return lastgatime; } ! //pj: Never gets called. ???? ! -(int)bitDistribution: (int *(*)[4])countptr cumulative: (BOOL)cum { BFCast * aForecast; unsigned int *agntcond; --- 1025,1113 ---- [aForecast setVariance: (1.0 - c)*[aForecast getVariance] + c*deviation]; } ! ! [aForecast setStrength: privateParams->maxdev ! - [aForecast getVariance] ! + [aForecast getSpecfactor]]; ! // ****************************************/ ! // pj: The preceeding is based on sfsm's bfagent.m ! // ! // original bfagent has this: rptr->strength = p->maxdev - ! // rptr->variance + rptr->specfactor; ! ! // BFagent in ASM-2.0 had this: fptr->strength = ! // fptr->specfactor/fptr->variance; I've spoken to Blake ! // LeBaron and we both like the old way and don't know why it ! // was changed. ! ! // I hasten to say that maxdev is the benchmark for ! // variance, and I wonder if maxdev would not be better ! // renamed maxdev-squared or maxvar. ! //******************************************/ } [index drop]; } ! return self; } ! /*"Returns the absolute value of realDeviation"*/ ! - (double)getDeviation { return fabs(realDeviation); } ! /*"Currently, does nothing, used only if their are ANNagents"*/ ! - updateWeights { return self; } ! /*"Returns the "condbits" variable from parameters: the number of ! condition bits that are monitored in the world, or 0 if ! condition bits aren't used. ! "*/ ! - (int)nbits { return privateParams->condbits; } ! /*"Returns the number of forecasts that are used. In the original ! design, this was a constant set in the parameters, although revision ! of the code for ASM-2.2 conceivably should allow agents to alter the ! number of forecasts they maintain."*/ ! - (int)nrules { return privateParams->numfcasts; } ! /*"Return the last time the Genetic Algorithm was run. ! // Agents that don't use a genetic algorithm return MININT. This ! // may be used to see if the bit distribution might have changed, ! // since a change can only occur through a genetic algorithm."*/ ! - (int)lastgatime { return lastgatime; } ! /*"Currently, this method is not called anywhere in ASM-2.2. It might ! serve some purpose, past or present, I don't know (pj: ! 2001-11-26)"*/ ! // Original docs from ASM-2.0 ! // Places in (*countptr)[0] -- (*countptr)[3] the addresses of 4 ! // arrays, (*countptr)[0][i] -- (*countptr)[3][i], which are filled ! // with the number of bits that are 00, 01, 10, or 11 respectively, ! // for each condition bit i= 0, 1, nbits-1, summed over all rules or ! // forecasters. Returns nbits, the number of condition bits. If ! // cum is YES, adds the new counts to whatever is in the (*coutptr) ! // arrays already. Agents that don't use condition bits return -1. ! // The 4-element array (*countptr)[4] must already exist, but the ! // arrays to which its element point are supplied dynamically. This ! // method must be provided by each subclass that has condition bits. ! - (int)bitDistribution: (int *(*)[4])countptr cumulative: (BOOL)cum { BFCast * aForecast; unsigned int *agntcond; *************** *** 1128,1135 **** return condbits; } ! //pj: this method was never called anywhere ! -(int)fMoments: (double *)moment cumulative: (BOOL)cum { BFCast *aForecast ; int i; --- 1168,1180 ---- return condbits; } ! ! //pj: this method was never called anywhere in ASM-2.0 ! ! /*"Currently, this method is not called anywhere in ASM-2.2. It might ! serve some purpose, past or present, I don't know (pj: ! 2001-11-26)"*/ ! - (int)fMoments: (double *)moment cumulative: (BOOL)cum { BFCast *aForecast ; int i; *************** *** 1158,1164 **** } //pj: this method is not called anywhere ! -(const char *)descriptionOfBit: (int)bit { if (bit < 0 || bit > getInt(privateParams,"condbits")) return "(Invalid condition bit)"; --- 1203,1218 ---- } //pj: this method is not called anywhere ! ! /*"Currently, this method is not called anywhere in ASM-2.2. It might ! serve some purpose, past or present, I don't know (pj: ! 2001-10-26)"*/ ! // ASM-2.0 documentation: ! // If the agent uses condition bits, returns a description of the ! // specified bit. Invalid bit numbers return an explanatory message. ! // Agents that don't use condition bits return NULL. ! // ! - (const char *)descriptionOfBit: (int)bit { if (bit < 0 || bit > getInt(privateParams,"condbits")) return "(Invalid condition bit)"; *************** *** 1167,1215 **** } ! // Genetic algorithm // ! // 1. MakePool() makes a list in reject[] of the "npool" weakest rules. // ! // 2. "nnew" new rules are created in newrules[], using tournament ! // selection, crossover, and mutation. "Tournament selection" ! // means picking two candidates purely at random and then choosing ! // the one with the higher strength. See the Crossover() and ! // Mutate() routines for more details about how they work. // ! // 3. The nnew new rules replace nnew of the npool weakest old ones found in ! // step 1. GetMort() is called for each of the nnew new rules and ! // selects one to replace out of the remainder of the original npool weak ! // ones. It pays no attention to strength, but looks at similarity of ! // the bitstrings -- rather like tournament selection, we pick two ! // candidates at random and choose the one with the MORE similar ! // bitstring to be replaced. This maintains more diversity. // ! // 4. Generalize() looks for rules that haven't been triggered for ! // "longtime" and generalizes them by changing a randomly chosen ! // fraction "genfrac" of 0/1 bits to "don't care". It does this ! // independently of strength to all rules in the population. // // Parameter list: - // - // npool -- size of pool of weakest rules for possible relacement; - // specified as a fraction of numfcasts by "poolfrac" - // nnew -- number of new rules produced - // specified as a fraction of numfcasts by "newfrac" - // pcrossover -- probability of running Crossover() at all. - // plinear -- linear combination "crossover" prob. - // prandom -- random from each parent crossover prob. - // pmutation -- per bit mutation prob. - // plong -- long jump prob. - // pshort -- short (neighborhood) jump prob. - // nhood -- size of neighborhood. - // longtime -- generalize if rule unused for this length of time - // genfrac -- fraction of 0/1 bits to make don't-care when generalising - performGA { - // register struct BF_fcast *fptr; - // struct BF_fcast *nr; register int f; int new; BFCast * parent1, * parent2; --- 1221,1301 ---- } ! /*" Genetic algorithm. It relies on the following separate methods. ! (pj: 2001-11-25. I still see some room for improvement here, but the ! emphasis is to eliminate all global variables and explicitly pass ! return values instead. Any values needed for computations should ! either be passed explicitly or taken from someplace safe) // ! // 1. MakePool makes a list of the weakest forecasts: ! // rejectList. That is the "npool" weakest rules. // ! // 2. "nnew" new rules are created. They are put into a Swarm list ! // called newList. Their bit settings are taken from either crossover ! // (using tournament selection to get parents), or mutation. ! // "Tournament selection" means picking two candidates purely at ! // random and then choosing the one with the higher strength. See ! // the Crossover and Mutate methods for more details about how they ! // work. // ! // 3. The nnew new rules replace weakest old ones found in step ! // 1. This is done by the method "TransferFcastsFrom:To:" It pays no ! // attention to strength, but looks at similarity of the bitstrings ! // -- rather like tournament selection, we pick two candidates from ! // the rejectList at random and choose the one with the MORE similar ! // bitstring to be replaced. This maintains more diversity. // ! // 4. Generalize looks for rules that haven't been triggered for ! // "longtime" and generalizes them by changing a randomly chosen ! // fraction "genfrac" of 0/1 bits to "don't care". It does this ! // independently of strength to all rules in the population. // + // There are several private methods that take care of this + // work. They don't show up in the public interface, but here they + // are: + + -(BFCast *) CopyRule:(BFCast *) to From: (BFCast *) from + + -(void) MakePool: rejects From: (id ) list + + -(BOOL) Mutate: (BFCast *) new Status: (BOOL) changed + + -(BFCast *) Crossover:(BFCast *) newForecast Parent1: (BFCast *) parent1 Parent2: (BFCast *) parent2 + + - (void) TransferFcastsFrom: newlist To: forecastList Replace: rejects + + - (BFCast *) GetMort: (BFCast *) new Rejects: (id ) rejects + + -(void) Generalize: (id) list AvgStrength: (double) avgstrength + // Parameter list: + _{npool -- size of pool of weakest rules for possible relacement; + specified as a fraction of numfcasts by "poolfrac"} + + _{nnew -- number of new rules produced + specified as a fraction of numfcasts by "newfrac"} + + _{ pcrossover -- probability of running Crossover.} + + _{plinear -- linear combination "crossover" prob.} + + // _{ prandom -- random from each parent crossover prob.} + + // _{ pmutation -- per bit mutation prob.} + + // _{ plong -- long jump prob.} + + // _{pshort -- short (neighborhood) jump prob.} + + // _{nhood -- size of neighborhood.} + + // _{longtime -- generalize if rule unused for this length of time} + + // _{ genfrac -- fraction of 0/1 bits to make don't-care when generalising} + "*/ - performGA { register int f; int new; BFCast * parent1, * parent2; *************** *** 1220,1242 **** double temp; //for holding values needed shortly //pj: previously declared as globals int * bitlist; ! //pj: could be only in perforGA, but then would be recreated every time. id newList = [List create: [self getZone]]; //to collect the new forecasts; id rejectList = [Array create: [self getZone] setCount: getInt(privateParams,"npoolmax")]; - - - static double avstrength; ++gacount; currentTime = getCurrentTime(); ! privateParams->lastgatime= params->lastgatime = lastgatime = currentTime; bitlist = privateParams->bitlist; // Find the npool weakest rules, for later use in TrnasferFcasts - //MakePool(fcastList); [self MakePool: rejectList From: fcastList]; --- 1306,1327 ---- double temp; //for holding values needed shortly //pj: previously declared as globals int * bitlist; ! id newList = [List create: [self getZone]]; //to collect the new forecasts; id rejectList = [Array create: [self getZone] setCount: getInt(privateParams,"npoolmax")]; + static double avstrength;//static inside a method has a different effect than static in a class ++gacount; currentTime = getCurrentTime(); ! //??Why is lastgatime in the params at all??? ! // privateParams->lastgatime= params->lastgatime = lastgatime = currentTime; ! lastgatime = currentTime; bitlist = privateParams->bitlist; // Find the npool weakest rules, for later use in TrnasferFcasts [self MakePool: rejectList From: fcastList]; *************** *** 1310,1316 **** [aNewForecast setLastactive: currentTime]; //following bfagent.m: varvalue = privateParams->maxdev-avstrength+[aNewForecast getSpecfactor]; ! if (varvalue < 0 ) raiseEvent(WarningMessage, "varvalue less than zero"); [aNewForecast setVariance: varvalue]; altvarvalue = [[fcastList atOffset: 0] getVariance]- madv; if ( varvalue < altvarvalue ) --- 1395,1401 ---- [aNewForecast setLastactive: currentTime]; //following bfagent.m: varvalue = privateParams->maxdev-avstrength+[aNewForecast getSpecfactor]; ! //if (varvalue < 0 ) raiseEvent(WarningMessage, "varvalue less than zero"); [aNewForecast setVariance: varvalue]; altvarvalue = [[fcastList atOffset: 0] getVariance]- madv; if ( varvalue < altvarvalue ) *************** *** 1322,1329 **** [newList addLast: aNewForecast]; //?? were these not initialized in original?// - //[aNewForecast print]; - // Pick first parent using touranment selection //pj: ??should this operate on all or only active forecasts??? do --- 1407,1412 ---- *************** *** 1337,1350 **** parent2 = [self Tournament: fcastList]; while (parent2 == parent1 || parent2 == nil) ; - // Crossover(aNewForecast,parent1, parent2); [self Crossover: aNewForecast Parent1: parent1 Parent2: parent2]; if (aNewForecast==nil) {raiseEvent(WarningMessage,"got nil back from crossover");} changed = YES; } else { - //pj: CopyRule(aNewForecast,parent1); [self CopyRule: aNewForecast From: parent1]; if(!aNewForecast)raiseEvent(WarningMessage,"got nil back from CopyRule"); --- 1420,1431 ---- *************** *** 1352,1365 **** } //It used to only do this if changed, but why not all?? - - // [aNewForecast print]; } while (0); /* Replace while(0) with while(!changed) to force diversity */ } // Replace nnew of the weakest old rules by the new ones - //pj: TransferFcasts ( newList, fcastList , rejectList); [self TransferFcastsFrom: newList To: fcastList Replace: rejectList]; --- 1433,1443 ---- *************** *** 1391,1400 **** ! /*------------------------------------------------------*/ ! /* CopyRule */ ! /*------------------------------------------------------*/ ! -(BFCast *) CopyRule:(BFCast *) to From: (BFCast *) from { [to setForecast: [from getForecast]]; [to setLforecast: [from getLforecast]]; --- 1469,1483 ---- ! /*"This is a method that copies the instance variables out of one ! forecast object into another. It copies not only the bitvector of ! monitored conditions, but also the forecast value, strength, ! variance, specFactor, specificity, and so forth. The only deviation ! is that if the return from the original forecast's getCnt method ! (its count value) is equal to 0, then the strength of the copy is ! equal to the value of a static variable named minstrength."*/ ! ! - (BFCast *)CopyRule: (BFCast *)to From: (BFCast *)from { [to setForecast: [from getForecast]]; [to setLforecast: [from getLforecast]]; *************** *** 1414,1425 **** } ! /*------------------------------------------------------*/ ! /* MakePool */ ! /*------------------------------------------------------*/ ! -(void) MakePool: rejects From: (id ) list { - register int top; int i,j = 0 ; BFCast * aForecast; --- 1497,1507 ---- } ! /*"Given a list of forecasts, find the worst ones and put them into a ! pool of rejects. This method requires 2 inputs, the name of the reject ! list (actually, a Swarm Array) and the Array of forecasts. "*/ ! - (void)MakePool: (id )rejects From: (id )list { register int top; int i,j = 0 ; BFCast * aForecast; *************** *** 1482,1488 **** /*------------------------------------------------------*/ /* Mutate */ /*------------------------------------------------------*/ ! -(BOOL) Mutate: (BFCast *) new Status: (BOOL) changed /* * For the condition bits, Mutate() looks at each bit with * probability pmutation. If chosen, a bit is changed as follows: --- 1564,1570 ---- /*------------------------------------------------------*/ /* Mutate */ /*------------------------------------------------------*/ ! - (BOOL)Mutate: (BFCast *)new Status: (BOOL)changed /* * For the condition bits, Mutate() looks at each bit with * probability pmutation. If chosen, a bit is changed as follows: *************** *** 1627,1633 **** /*------------------------------------------------------*/ /* Crossover */ /*------------------------------------------------------*/ ! -(BFCast *) Crossover:(BFCast *) newForecast Parent1: (BFCast *) parent1 Parent2: (BFCast *) parent2 /* * On the condition bits, Crossover() uses uniform crossover -- each * bit is chosen randomly from one parent or the other. --- 1709,1715 ---- /*------------------------------------------------------*/ /* Crossover */ /*------------------------------------------------------*/ ! - (BFCast *)Crossover: (BFCast *)newForecast Parent1: (BFCast *)parent1 Parent2: (BFCast *)parent2 /* * On the condition bits, Crossover() uses uniform crossover -- each * bit is chosen randomly from one parent or the other. *************** *** 1668,1692 **** if (value > 0) [newForecast incrSpecificity]; } } - //pj:???wont those changes automatically show up in newForecast??// - - // checked with Blake: this was a remnant, only need first if, as above - // if(irand(1)==0) - // { - // for (word = 0; word ) rejects /* GetMort() selects one of the npool weak old fcasts to replace * with a newly generated rule. It pays no attention to strength, * but looks at similarity of the condition bits -- like tournament --- 1842,1848 ---- /*------------------------------------------------------*/ /* GetMort */ /*------------------------------------------------------*/ ! - (BFCast *)GetMort: (BFCast *)new Rejects: (id )rejects /* GetMort() selects one of the npool weak old fcasts to replace * with a newly generated rule. It pays no attention to strength, * but looks at similarity of the condition bits -- like tournament *************** *** 1866,1886 **** aReject = [rejects atOffset: r2]; [ rejects atOffset: r2 put: nil] ; } - /* - fptr = reject[r1]; - reject[r1] = NULL; - */ - /* - if(reject[r1]->count < reject[r2]->count) { - fptr = reject[r1]; - reject[r1] = NULL; - } - else { - fptr = reject[r2]; - reject[r1] = NULL; - } - */ - return aReject; } --- 1917,1922 ---- *************** *** 1889,1895 **** /*------------------------------------------------------*/ /* Generalize */ /*------------------------------------------------------*/ ! -(void) Generalize: (id) list AvgStrength: (double) avgstrength /* * Each forecast that hasn't be used for longtime is generalized by * turning a fraction genfrac of the 0/1 bits to don't-cares. --- 1925,1931 ---- /*------------------------------------------------------*/ /* Generalize */ /*------------------------------------------------------*/ ! - (void)Generalize: (id)list AvgStrength: (double)avgstrength /* * Each forecast that hasn't be used for longtime is generalized by * turning a fraction genfrac of the 0/1 bits to don't-cares. *************** *** 1900,1908 **** int bit, j; BOOL changed; // int currentTime; ! int * bitlist=NULL; ! bitlist = [privateParams getBitListPtr]; currentTime = getCurrentTime(); --- 1936,1944 ---- int bit, j; BOOL changed; // int currentTime; ! int * bitlist = NULL; ! bitlist = [privateParams getBitListPtr]; currentTime = getCurrentTime(); *************** *** 1929,1955 **** } if (changed) { [aForecast setCnt: 0]; [aForecast setLastactive: currentTime]; ! [aForecast updateSpecfactor]; ! [aForecast setVariance: [aForecast getSpecfactor] / avgstrength]; ! [aForecast setStrength: [aForecast getSpecfactor]/[aForecast getVariance]]; ! //??????????????????????? ! //??bfagent has: ! /* rptr->specfactor = (condbits - pp->nnulls - rptr->specificity)* ! pp->bitcost; ! medvar = pp->maxdev-medianstrength+rptr->specfactor; ! if (medvar >= 0.0) ! rptr->variance = medvar; ! rptr->strength = medianstrength;*/ ! } } } } ! //pj: in case you want to see the 0101 representation of an integer: - printcond: (int) word { int i; --- 1965,1996 ---- } if (changed) { + double varvalue; [aForecast setCnt: 0]; [aForecast setLastactive: currentTime]; ! [aForecast updateSpecfactor]; ! //ASM2.0 would be like this: ! // [aForecast setVariance: [aForecast getSpecfactor] / avgstrength]; ! //ASM2.0 would have us do it like this: ! //[aForecast setStrength: [aForecast getSpecfactor]/[aForecast getVariance]]; ! ! //I rather think that, following sfsm, it would be this: ! varvalue = privateParams->maxdev - avgstrength + [aForecast getSpecfactor]; ! if (varvalue >0 ){ ! [aForecast setVariance: varvalue]; ! } ! //apparently don't change variance otherwise ! ! [aForecast setStrength: avgstrength]; } } } } ! /*"in case you want to see the 0101 representation of an ! integer. Sometimes this comes in handy if you are looking at a ! particular forecast's value as an int and you need to convert it to ! the 0's and 1's"*/ - printcond: (int) word { int i; *************** *** 1967,1972 **** --- 2008,2017 ---- return self; } + /*"This is a general utility method for Swarm lists. It removes all + objects form the "outputList" and copies the elements from list into + it. It does not actually destroy any elements from either list, it + just updates references."*/ - copyList: list To: outputList { id index, anObject; diff -cr ASM-20011125/BitVector.h ASM-2.2/BitVector.h *** ASM-20011125/BitVector.h Fri Jun 2 11:03:37 2000 --- ASM-2.2/BitVector.h Thu Nov 1 21:42:12 2001 *************** *** 2,10 **** @interface BitVector: SwarmObject { ! unsigned int *conditions; ! int condwords; ! int condbits; } --- 2,10 ---- @interface BitVector: SwarmObject { ! int condwords; /*"Number of words of memory required to hold bits in this model"*/ ! int condbits; /*"The number of conditions bits we expect to actually use"*/ ! unsigned int *conditions; /*points to a dynamically allocated array of "condwords" elements"*/ } *************** *** 12,43 **** + init; ! -(void) setCondwords: (int) x; ! -(void) setCondbits: (int) x; ! -(void) setConditions: (int *) x; ! -(int *) getConditions; ! -(void) setConditionsWord: (int) i To: (int) value; ! -(int) getConditionsWord: (int) x; ! -(void) setConditionsbit: (int) bit To: (int) x; ! -(int) getConditionsbit: (int)bit; ! -(void) setConditionsbitToThree: (int) bit; ! -(void) switchConditionsbit: (int) bit; ! -(void) setConditionsbit: (int) bit FromZeroTo: (int) x; ! -(void) maskConditionsbit: (int) bit; ! - (void) drop; ! - printcond: (int) word; @end --- 12,43 ---- + init; ! - (void)setCondwords: (int)x; ! - (void) setCondbits: (int)x; ! - (void)setConditions: (int *)x; ! - (int *)getConditions; ! - (void)setConditionsWord: (int)i To: (int)value; ! - (int)getConditionsWord: (int)i; ! - (void)setConditionsbit: (int)bit To: (int)x; ! - (int)getConditionsbit: (int)bit; ! - (void)setConditionsbitToThree: (int)bit; ! - (void)switchConditionsbit: (int)bit; ! - (void)setConditionsbit: (int)bit FromZeroTo: (int)x; ! - (void)maskConditionsbit: (int)bit; ! - (void)drop; ! - printcond: (int)word; @end diff -cr ASM-20011125/BitVector.m ASM-2.2/BitVector.m *** ASM-20011125/BitVector.m Fri Jun 2 11:03:37 2000 --- ASM-2.2/BitVector.m Tue Nov 27 21:03:34 2001 *************** *** 14,19 **** --- 14,104 ---- static void makebittables(void); @implementation BitVector + /*" This class is the "hairy guts" that makes bit forecasts possible. + + A bit vector is a group of "words", and each "word" contains 16 + indicators. In this model, the bit vectors have 5 words, which means + there are 80 indicators possible. + + In the substance of this model, a "bit" is an aspect of the world + being monitored. In genetic algorithm terms, one can say "NO", "YES" + or "don't care", for each piece of information. A bit has values in + integer format of 0, 1, or 2. But in binary, that is 00, 01, and 10, + and in this class those binary representations are clumped together to + be represented by a 32 bit integer, as in + + 0110001000011000010101100100100101010101 + + which holds the status of 16 bits. There can be as many as 5 of these + in a BitVector. Those words are referred to by the pointer + "conditions". The first word can be found at conditions[0], the + second at conditions[1], and so forth. Note that these are integer + values, but the bit math does work on the binary values. I probably + need a computer scientist to translate this for me... + + The world in the ASM can give a vector as well, telling us in binary + many indicators, whether they are good or bad, 0 or 1. So the bit + forecasting agent takes the 0's and 1's from the world, and checks to + see if they are used in the forecast, and makes a forecast. All of + the checking and setting of forecast bits is handled by this BitVector + class. + + I suppose, if you are like me and don't like bit math, this is all + confusing and you don't care, in which case you can readily ignore the + details and just proceed to set the values of bits according to the + interface below. Its pretty obvious. + + But if you want details, here is a very telling piece of documentation + that goes with the function "makebittables", which used to be at the + top of BFagent, but now its here, hidden in the dark and not so scary + to users: + + * + * Construct tables for fast bit packing and condition checking for + * classifier systems. Assumes 32 bit words, and storage of 16 ternary + * values (0, 1, or *) per word, with one of the following codings: + * Value Message-board coding Rule coding + + * 0 2 1 + * 1 1 2 + * * - 0 + + * Thus rule satisfaction can be checked with a simple AND between + * the two types of codings. + * + * Sets up the tables to store MAXCONDBITS ternary values in + * CONDWORDS = ceiling(MAXCONDBITS/16) words. + * + * After calling this routine, given an array declared as + * int array[CONDWORDS]; + * you can do the following: + * + * a. Store "value" (0, 1, 2, using one of the codings above) for bit n with + * array[WORD(n)] |= value << SHIFT[n]; + * if the stored value was previously 0; or + * + * b. Store "value" (0, 1, 2, using one of the codings above) for bit n with + * array[WORD(n)] = (array[WORD(n)] & NMASK[n]) | (value << SHIFT[n]); + * if the initial state is unknown. + * + * c. Store value 0 for bit n with + * array[WORD(n)] &= NMASK[n]; + * + * d. Extract the value of bit n (0, 1, 2, or possibly 3) with + * value = (array[WORD(n)] >> SHIFT[n]) & 3; + * + * e. Test for value 0 for bit n with + * if ((array[WORD(n)] & MASK[n]) == 0) ... + * + * f. Check whether a condition is fulfilled (using the two codings) with + * for (i=0; i> SHIFT[bit]) &3; return value; } ! -(void) setConditionsbitToThree: (int) bit { conditions[WORD(bit)] |= MASK[bit]; } ! ! -(void) maskConditionsbit: (int) bit { conditions[WORD(bit)] &= NMASK[bit]; // specificity --; } ! -(void) switchConditionsbit: (int) bit { conditions[WORD(bit)] ^= MASK[bit]; } ! ! - (void) drop { [[self getZone] freeBlock: conditions blockSize: condwords*sizeof(unsigned int) ]; [super drop]; } ! ! - printcond: (int) word { int i; int n = sizeof(int) * CHAR_BIT; --- 111,214 ---- return self; } ! /*"init runs the makebittables function, which creates some statically allocated vectors that are used in bit math."*/ ! + init { makebittables(); return self; } ! ! /*"Sets the number of words-worth of memory will be used"*/ ! - (void)setCondwords: (int)x { condwords = x; } ! /*"Sets the number of bits that this bit vector is supposed to take care of"*/ ! - (void) setCondbits: (int)x { condbits=x; } ! ! /*"Suppose a pointer to a set of conditions, x, already exists. This ! method takes that pointer and then copies its values into the ! conditions of the current bit vector"*/ ! ! - (void)setConditions: (int *)x ! { ! int i; ! for(i=0;i> SHIFT[bit]) &3; return value; } ! /*"The value 3 is used to indicate that a bit is not in use"*/ ! - (void)setConditionsbitToThree: (int)bit { conditions[WORD(bit)] |= MASK[bit]; } ! - (void)maskConditionsbit: (int)bit { conditions[WORD(bit)] &= NMASK[bit]; // specificity --; } ! /*"If the bit is 1, change it to 2, or vice versa"*/ ! - (void)switchConditionsbit: (int)bit { conditions[WORD(bit)] ^= MASK[bit]; } ! /*"Release freed memory"*/ ! - (void)drop { [[self getZone] freeBlock: conditions blockSize: condwords*sizeof(unsigned int) ]; [super drop]; } ! /*"Dump the current conditions to the screen. Use for debugging"*/ ! - printcond: (int)word { int i; int n = sizeof(int) * CHAR_BIT; *************** *** 130,136 **** ! static void makebittables() //declared in BFagent.m /* * Construct tables for fast bit packing and condition checking for * classifier systems. Assumes 32 bit words, and storage of 16 ternary --- 228,234 ---- ! static void makebittables() /* * Construct tables for fast bit packing and condition checking for * classifier systems. Assumes 32 bit words, and storage of 16 ternary *************** *** 170,177 **** * for (i=0; i + + * ASMModelSwarm.m ([ASMModelSwarm -buildObjects]): No need to set + world or agentList in Specialist anymore. See below where performTrading + and completeTrading are discussed. + + * Specialist.h: changed performTrading method to performTrading: + Market: and completeTrades is now completeTrades: Market:. This + way, the agent list and the market are explicitly passed in every + time. There is no need for Specialist class to have IVARs world + and agentList, so now the Specialist class can be archived without + any hassle. That is, putShallow should work. + + 2001-11-08 + + * Output.m ([Output -writeData]): Necessary to have hdf5 archives + putDeep because they can't save ordinary C arrays otherwise. + + 2001-11-06 + + * World.m (changes): Converted pricehistory and divhistory back to + dynamically allocated arrays. This was done because the Swarm + archiver was writing those arrays at every timestep, massively + filling up filespace. Don't know how to selectively write + variables from objects yet. + + 2001-11-03 + + * BFagent.m ([BFagent -initForecasts]): Now privateParams is a + truely separate instance of BFParams, one for each object. The old + way of using the same instance for all agents seemed fraught with + danger. + + * BFParams.m ([BFParams -copy:aZone]): New method to create a copy + of an instance and return it. Does detailed copy of all ivars, + including dynamically allocated arrays. + ([BFParams -copyProbList:p:size]): used in copy: method. This takes + a pointer to a double array and replaced the problist with it. + ([BFParams -copyBitList:Length:size]): copies a bitlist and replaces + existing bitlist with it. + + 2001-11-02 + + * BFParams.m ([BFParams -init]): Commented out "myWorld" array, + couldn't find any place it was used. Also commented out + lastgatime, couldn't figure why that value was kept here in the + parameters object, rather than in the BFagent. Maybe there's a + reason, but I can't find it. + + * ASMObserverSwarm.m ([ASMObserverSwarm -expostParamWrite]): fixed + expostParamWrite error (due to unavailability of time at + shutdown). + ([ASMObserverSwarm -buildObjects]): show probe map for BFParams + object. + + 2001-11-02 Paul E. Johnson + + * ASMObserverSwarm.m ([ASMObserverSwarm -buildObjects]): create + IVAR output, an aOutput object. This must exist as soon as the GUI + comes up, because the GUI buttons allow people to write + output. So, the output has to exist before the control panel stops + the simulation, and then the output object must be passed along to + ASMModelSwarm, where in turn the buildObjects method sticks some + things into the Output object. This is at the outer limits of my + patience. + ([ASMObserverSwarm -drop]): make sure the expostParamWrite method + is called, to save a copy of parameters at shutdown time if + writeParams==1; + + * Output.m ([Output -initializeHDFWriter]): Trying to stop the + nuisance of the empty hdfGraph file that was created from runs + that had data writing turned off. + + * ASMObserverSwarm.m ([ASMObserverSwarm -buildObjects]): put + control panel set state stopped before model swarm is + created. That way, any magic done in Parameters class will be + taken into account when the model is created. + + 2001-10-26 Paul E Johnson + + * ASMModelSwarm.h: delete warmupStepDividend and warmupStepPrice. + Delete the -initPeriod: method. It was superflous + + * ASMModelSwarm.m ([ASMModelSwarm -initPeriod:]): This method is + unnecessary. It can be replaced by a createAction in time 0 to the + startupSchedule. Note memory leaks of undropped objects in the + warmUp: method. + ([ASMModelSwarm -doWarmupStep]): new method to replace the 3 + separate methods, + + - warmupStepDividend; + - warmupStepPrice; + + I verified that the simulation is exactly the same after this + change. + + * ASMModelSwarm.h: convert warmUp and initPeriod to methods. Don't + know why they were C functions before. Did Autodoc documentation. + + 2001-10-26 + + * BFagent.h (Agent): nactive removed from IVAR list, it is used + only in prepareForTrading, so it is defined there. + + 2001-10-25 + + * World.m (GETMA): GETMA(ma,i) is a macro that tells the given ma + whether to get the exponential or regular ma. IT gets it from the + i'th element of the array of moving averages. + + * World.h (SwarmObject): delete awieght, bweight. That gets taken + care of byMovingAverage now. priceMA, divMA, oldpriceMA, olddivMA + objects now replace old mas. These MA objects keep both regular and + exponential MAs, and retrieve them with getMA and getEWMA. + + 2001-10-25 + + * World.m: Moved define NMAS into World.h, converted pointers to + arrays[NMAS], got rid of alloc's in World.m. + + * World.m: Moved define UPDOWNLOOKBACK into World.h, + converted pointers to arrays[NMAS], got rid of alloc's in World.m. + + 2001-10-25 Paul E Johnson * BFagent.m ([BFagent -CopyRule:to:from]): minstrength has to be diff -cr ASM-20011125/Dividend.h ASM-2.2/Dividend.h *** ASM-20011125/Dividend.h Sun May 7 13:09:26 2000 --- ASM-2.2/Dividend.h Fri Oct 26 11:46:52 2001 *************** *** 4,35 **** @interface Dividend: SwarmObject { ! int period; ! double baseline; ! double mindividend; ! double maxdividend; ! double amplitude; double deviation; double rho; double gauss; - double dvdnd; ! ! id normal; } ! -initNormal; //These member functions just take parameters set in the ! -setBaseline: (double)theBaseline; ! -setmindividend: (double)minimumDividend; ! -setmaxdividend: (double)maximumDividend; ! -(double)setAmplitude: (double)theAmplitude; ! -(int)setPeriod: (int)thePeriod; ! -setDerivedParams; ! -(double)dividend; @end --- 4,43 ---- @interface Dividend: SwarmObject { ! ! double baseline; /*"The centerline around which deviations are computed. ! // This is equal to the mean for a symmetric process ! // (i.e., if asymmetry = 0). "baseline" is set only ! // from the parameter file, and should NOT normally ! // be changed from the default value (10.0)."*/ ! // ! ! double amplitude; /*"The amplitude of the deviations from the baseline. ! // Measured in units of "baseline". The standard ! // deviation of the process is proportional to this."*/ + int period; /*"The period or auto-correlation time of the process."*/ + + double mindividend; /*"floor under dividend values"*/ + double maxdividend; /*"ceiling for dividend values"*/ double deviation; double rho; double gauss; double dvdnd; ! id normal; /*"A Swarm Normal Generator object"*/ } ! - initNormal; //These member functions just take parameters set in the ! - setBaseline: (double)theBaseline; ! - setmindividend: (double)minimumDividend; ! - setmaxdividend: (double)maximumDividend; ! - (double)setAmplitude: (double)theAmplitude; ! - (int)setPeriod: (int)thePeriod; ! - setDerivedParams; ! - (double)dividend; @end diff -cr ASM-20011125/Dividend.m ASM-2.2/Dividend.m *** ASM-20011125/Dividend.m Wed Oct 24 18:26:08 2001 --- ASM-2.2/Dividend.m Fri Oct 26 11:46:52 2001 *************** *** 1,40 **** // The Santa Fe Stockmarket -- Implementation of the dividend process. - // This object produces a stochastic sequence of dividends. The process - // is independent of the market and agents, depending only the parameters - // that are set for the dividend process (and on the random number generator). - - // -(double)dividend; - // Returns the next value of the dividend. This is the core method - // of the Dividend object, for which all else exists. It does NOT - // use the global time, but simply assumes that one period - // passes between each call. - // - // These processes are parameterized by some or all of the - // following parameters: - // - // baseline The centerline around which deviations are computed. - // This is equal to the mean for a symmetric process - // (i.e., if asymmetry = 0). "baseline" is set only - // from the parameter file, and should NOT normally - // be changed from the default value (10.0). - // - // amplitude The amplitude of the deviations from the baseline. - // Measured in units of "baseline". The standard - // deviation of the process is proportional to this. - // - // period The period or auto-correlation time of the process. - // - // - // -(double)setAmplitude: (double)theAmplitude - // Sets the "amplitude" parameter. See "-setDivType:". Returns the - // value actually set, which may be clipped or rounded compared to the - // supplied argument. - // // -(int)setPeriod: (int)thePeriod ! // Sets the "period" parameter. See "-setDivType:". Returns the ! // value actually set, which may be clipped compared to the ! // supplied argument. #import "Dividend.h" --- 1,7 ---- // The Santa Fe Stockmarket -- Implementation of the dividend process. // -(int)setPeriod: (int)thePeriod ! // #import "Dividend.h" *************** *** 43,55 **** #include #include - // Constants - #define PI 3.14159265 @implementation Dividend ! //pj: new method - initNormal { --- 10,25 ---- #include #include @implementation Dividend + /*" + // This object produces a stochastic sequence of dividends. The process + // is independent of the market and agents, depending only the parameters + // that are set for the dividend process (and on the random number generator). + "*/ ! /*"Creates a Swarm Normal Distribution object"*/ - initNormal { *************** *** 79,84 **** --- 49,58 ---- } + /*" Sets the "amplitude" parameter. Returns the + // value actually set, which may be clipped or rounded compared to the + // supplied argument. See "-setDivType:". + "*/ -(double)setAmplitude:(double)theAmplitude { amplitude = theAmplitude; *************** *** 90,95 **** --- 64,72 ---- return amplitude; } + /*" Sets the "period" parameter. Returns the + // value actually set, which may be clipped compared to the supplied + // argument. See "-setDivType:". "*/ -(int)setPeriod: (int)thePeriod { *************** *** 118,131 **** return self; } -(double)dividend - /* - * Compute dividend for the current period. - * Assumes that one period passes between each call; note that "time" - * may not be the same as the global variable "t" because shifts are - * introduced to maintain phase when certain parameters are changed. - */ { //pj: // dvdnd = baseline + rho*(dvdnd - baseline) + gauss*normal(); --- 95,108 ---- return self; } + /*" Returns the next value of the dividend. This is the core method + of the Dividend object, for which all else exists. It does NOT use + the global time, but simply assumes that one period passes between + each call. Note that "time" may not be the same as the global + variable "t" because shifts are introduced to maintain phase when + certain parameters are changed."*/ -(double)dividend { //pj: // dvdnd = baseline + rho*(dvdnd - baseline) + gauss*normal(); diff -cr ASM-20011125/Makefile ASM-2.2/Makefile *** ASM-20011125/Makefile Fri Jun 29 16:30:15 2001 --- ASM-2.2/Makefile Tue Dec 4 14:53:58 2001 *************** *** 7,13 **** OBJECTS = Agent.o Dividend.o World.o \ Specialist.o Output.o ASMModelSwarm.o \ ASMObserverSwarm.o ASMBatchSwarm.o main.o BFParams.o BFCast.o BFagent.o BitVector.o \ ! ASMModelParams.o Parameters.o OTHERCLEAN = param.data_* output.data* DATAFILES = batch.setup param.data --- 7,13 ---- OBJECTS = Agent.o Dividend.o World.o \ Specialist.o Output.o ASMModelSwarm.o \ ASMObserverSwarm.o ASMBatchSwarm.o main.o BFParams.o BFCast.o BFagent.o BitVector.o \ ! ASMModelParams.o Parameters.o MovingAverage.o OTHERCLEAN = param.data_* output.data* DATAFILES = batch.setup param.data *************** *** 21,27 **** Output.o: Output.h Output.m BFParams.h ASMModelParams.h ASMModelSwarm.o: ASMModelSwarm.h ASMModelSwarm.m BFParams.o Specialist.o: Specialist.h Specialist.m ! World.o: World.h World.m ASMModelSwarm.o: ASMModelSwarm.h ASMModelSwarm.m Output.h BFParams.h Specialist.h Dividend.h World.h BFagent.h Agent.h ASMObserverSwarm.o: ASMObserverSwarm.h ASMObserverSwarm.m ASMBatchSwarm.o: ASMBatchSwarm.h ASMBatchSwarm.m --- 21,27 ---- Output.o: Output.h Output.m BFParams.h ASMModelParams.h ASMModelSwarm.o: ASMModelSwarm.h ASMModelSwarm.m BFParams.o Specialist.o: Specialist.h Specialist.m ! World.o: World.h World.m MovingAverage.h ASMModelSwarm.o: ASMModelSwarm.h ASMModelSwarm.m Output.h BFParams.h Specialist.h Dividend.h World.h BFagent.h Agent.h ASMObserverSwarm.o: ASMObserverSwarm.h ASMObserverSwarm.m ASMBatchSwarm.o: ASMBatchSwarm.h ASMBatchSwarm.m *************** *** 30,32 **** --- 30,34 ---- BitVector.o: BitVector.h BitVector.m ASMModelParams.o: ASMModelParams.h ASMModelParams.m Parameters.o: Parameters.h Parameters.m ASMModelParams.h BFParams.h + MovingAverage.o: MovingAverage.h MovingAverage.m + Only in ASM-2.2: MovingAverage.h Only in ASM-2.2: MovingAverage.m diff -cr ASM-20011125/Output.h ASM-2.2/Output.h *** ASM-20011125/Output.h Wed Oct 24 18:26:08 2001 --- ASM-2.2/Output.h Tue Nov 6 01:29:38 2001 *************** *** 14,38 **** @interface Output: SwarmObject { @private ! BOOL dataFileExists; ! World * outputWorld; ! Specialist * outputSpecialist; ! id archiver, dataArchiver; - // FILE * paramOutputFile; - time_t runTime; - char timeString[100]; - - FILE * dataOutputFile; - id hdfWriter; - id hdf5container; - @public ! int currentTime; ! double price; ! double dividend; ! double volume; } --- 14,41 ---- @interface Output: SwarmObject { @private ! BOOL dataFileExists; /*"Indicator that dataFile initialization has alreadyoccurred"*/ ! World * outputWorld; /*"Reference to the world, where we can get data!"*/ ! Specialist * outputSpecialist; /*" Reference to the Specialist object, where we can get data!"*/ ! id archiver, dataArchiver; /*"hdf5 or LISP objects, depending on the CPP flags"*/ ! ! time_t runTime; /*"Return from the systems time() function"*/ ! char timeString[100];/*"a verbose description of current time"*/ ! ! FILE * dataOutputFile; /*"FILE handle for output from C style fprintf"*/ ! id hdfWriter; /*"EZGraph object that is used only to create hdf5 formatted output"*/ ! id hdf5container; /*"HDF5 data container object used by hdfWriter"*/ @public ! int currentTime; /*"current time of simulation"*/ ! ! // These IVARs were previously used for record keeping, but they ! // are not needed now. Could reinstate and add "self" to the ! // objects that the archiver dumps out. ! // double price; /*"current price"*/ ! // double dividend; /*"current dividend"*/ ! // double volume; /*"current volume"*/ } *************** *** 44,49 **** --- 47,54 ---- -prepareOutputFile; + -(void) initializeHDFWriter; + -writeData; -(void) drop; diff -cr ASM-20011125/Output.m ASM-2.2/Output.m *** ASM-20011125/Output.m Wed Oct 24 18:26:08 2001 --- ASM-2.2/Output.m Thu Nov 8 08:34:52 2001 *************** *** 2,9 **** --- 2,92 ---- #include // stdio, time + + + /*" + To show the possible data output tools, I have 3 + different ways of saving the output time streams from the model. All + three should be similar/equivalent representations of the numbers. + + 1) Text output of data streams. + 2) HDF5 or LISP format output of object dumps from a "putShallow" + call to a data archiver object. This dumps full snapshots of + the world and the specialist into a LISP on hdf5 archive. + 3) HDF5 output EZGraph which writes one vector per plotted line + into an hdf5 file. + + This code has a preprocessor flag to control the behavior of data + storage. If compile without any CPP flags, then the data files are + saved in the .scm format, which is "scheme". Otherwise, use the flag + NO_LISP, and it uses hdf5 format. In Swarm, that means you type the + make command: + + make EXTRACPPFLAGS=-DNO_LISP + + + The buttons in the ASMObserverSwarm display turn on data saving. Look + for "writeSimulationParams" and the other "toggleDataWrite". These + were in the original ASM Swarm model, but I've replaced the + functionality with the newer storage methods. The data is saved only + if you turn on the writeData option. If "toggleDataWrite" is empty or + false, hit that button and it shows "true". When the model runs, + output will be created. If you run the program in batch mode, it + automatically turns on the data writing. + + Please note that if you want the simulation to save your parameter + values to a file, you can click the GUI button + "writeSimulationParams." If you push that button, the system writes + the parameter values into a file, such as + + guiSettingsThu_Jun_28_23_48_00_2001.scm + + + if you did not compile with the NO_LISP flag. Otherwise you get a + .hdf file. One key change from the old ASM is that you can push that + button at time 0, and it will save a snap at that time, and any time + you stop the model, you can change parameters and punch the button + again, and it will also save a snapshot at quit time. I believe this + works fine now, but it was a little tricky making sure the objects are + created in the right order and early enough to allow this to work. + + Now, just a word about data formatting. Because it is familiar and + compatible with existing programs, I often prefer to save data in raw + ASCII format. In case you want text output, this shows you how to do it. + I think filenames that have the date in them are good to + help remember when they were originally created, for example. It + creates an ASCII file, for example, + + output.data_Thu_Jun_28_23_48_00_2001 + + + However, I understand the reasons others are pushing to use more + refined formats. Many people are digging into hdf5 format for data + storage, and I've taken a look at that too. I took the easy road and + just dumped the whole world and specialist class with swarm's + archiver. It seems to work great?! The output file is called + something like + + swarmDataArchiveFri_Jun_29_16_29_25_2001.hdf + or + swarmDataArchiveFri_Jun_29_16_22_59_2001.scm + + You note here that output uses the current time and date to write the + output file names. Today I ran an example and ended up with these + three files of output: + + output.dataWed_Oct_24_11_30_18_2001 + swarmDataArchiveWed_Oct_24_11_30_18_2001.scm + hdfGraphWed_Oct_24_11_30_18_2001.hdf + "*/ @implementation Output + + /*"createEnd does a lot of specific things that make the data output + objects work. It gets the system time, uses that to fashion a + filename that includes the time, then where necessary it creates + archivers which will later be called on to get readings on the + system and record them."*/ - createEnd { int i; *************** *** 12,19 **** char dataArchiveName[100]; - char hdfEZGraphName[100]; - if(!runTime) runTime = time(NULL); dataFileExists = NO; --- 95,100 ---- *************** *** 41,50 **** strcpy (dataArchiveName,"swarmDataArchive"); strcat (dataArchiveName, timeString); - strcpy (hdfEZGraphName,"hdfGraph"); - strcat (hdfEZGraphName, timeString); - strcat (hdfEZGraphName, ".hdf"); - #ifdef NO_LISP strcat (paramFileName,".hdf"); strcat (dataArchiveName,".hdf"); --- 122,127 ---- *************** *** 64,110 **** #endif ! { ! hdf5container = [HDF5 createBegin: [self getZone]]; ! [hdf5container setWriteFlag: YES]; ! [hdf5container setName: hdfEZGraphName]; ! hdf5container = [hdf5container createEnd]; ! ! hdfWriter = [EZGraph create: [self getZone] ! setHDF5Container: hdf5container ! setPrefix: "market"]; ! } ! return self; } ! - setSpecialist: (Specialist *)theSpec { outputSpecialist = theSpec; - - [hdfWriter createSequence: "volume" - withFeedFrom: outputSpecialist - andSelector: M(getVolume)]; return self; } - setWorld: (World *)theWorld; { outputWorld = theWorld; - - - [hdfWriter createSequence: "price" - withFeedFrom: outputWorld - andSelector: M(getPrice)]; - - - [hdfWriter createSequence: "dividend" - withFeedFrom: outputWorld - andSelector: M(getDividend)]; return self; } - writeParams: modelParam BFAgent: bfParms Time: (long int) t { char modelKey[20]; --- 141,166 ---- #endif ! return self; } ! /*"The output object needs to have a reference to a Specialist object, from whom it can gather data on the volume of trade."*/ - setSpecialist: (Specialist *)theSpec { outputSpecialist = theSpec; return self; } + /*"The output object must have a reference to a World object, from which it can get price, dividend, or any other information it wants"*/ - setWorld: (World *)theWorld; { outputWorld = theWorld; return self; } + /*"This flushes a snapshot of the current parameter settings from + both the ASMModelParams and BFAgentParams into a file"*/ - writeParams: modelParam BFAgent: bfParms Time: (long int) t { char modelKey[20]; *************** *** 119,134 **** [archiver sync]; #endif ! [archiver putShallow: paramKey object: bfParms]; #ifndef NO_LISP ! [archiver sync]; #endif ! return self; } ! //Setup the output file. - prepareOutputFile { char outputFile[256]; --- 175,194 ---- [archiver sync]; #endif ! [archiver putShallow: paramKey object: bfParms]; #ifndef NO_LISP ! [archiver sync]; #endif ! return self; } ! /*"Because it is possible for users to turn on data writing during a ! run of the simulation, it is necessary to have this method which can ! initialize the data output files. Each time this is called, it ! checks to see if the files have already been initialized. That way ! it does not initialize everything twice."*/ - prepareOutputFile { char outputFile[256]; *************** *** 145,156 **** fprintf (dataOutputFile, "currentTime\t price\t\t dividend\t volume\n\n"); dataFileExists = YES; } - - return self; } -writeData { --- 205,255 ---- fprintf (dataOutputFile, "currentTime\t price\t\t dividend\t volume\n\n"); dataFileExists = YES; } return self; } + /*"In case the Output class is told to write output data and there is + no hdfWriter object yet, then this method will be run. I could not + find another way to make sure that this object was not created unless + the system actually tries to write data. It cannot be done in the + prepareOutputFile method, because that method can be called from the + GUI before the Specialist and World objects exist. Those objects are + necessary to initialize an hdfWriter, as seen in this code. "*/ + + -(void) initializeHDFWriter + { + + char hdfEZGraphName[100]; + + strcpy (hdfEZGraphName,"hdfGraph"); + strcat (hdfEZGraphName, timeString); + strcat (hdfEZGraphName, ".hdf"); + + hdf5container = [HDF5 createBegin: [self getZone]]; + [hdf5container setWriteFlag: YES]; + [hdf5container setName: hdfEZGraphName]; + hdf5container = [hdf5container createEnd]; + + hdfWriter = [EZGraph create: [self getZone] + setHDF5Container: hdf5container + setPrefix: "market"]; + [hdfWriter createSequence: "volume" + withFeedFrom: outputSpecialist + andSelector: M(getVolume)]; + + + [hdfWriter createSequence: "price" + withFeedFrom: outputWorld + andSelector: M(getPrice)]; + + [hdfWriter createSequence: "dividend" + withFeedFrom: outputWorld + andSelector: M(getDividend)]; + + } + + /*"The write data method dumps out measures of the price, dividend, and volume indicators into several formats"*/ -writeData { *************** *** 179,208 **** sprintf (worldName, "world%ld",t); sprintf (specName, "specialist%ld",t); - [dataArchiver putShallow: worldName object: outputWorld]; #ifndef NO_LISP [dataArchiver sync]; #endif [dataArchiver putShallow: specName object: outputSpecialist]; #ifndef NO_LISP [archiver sync]; #endif ! // Third, now use the EZGraph dump of its time strings. ! [hdfWriter step]; ! return self; } ! -(void) drop { ! [hdfWriter drop]; [archiver drop]; [dataArchiver drop]; [super drop]; } - @end --- 278,315 ---- sprintf (worldName, "world%ld",t); sprintf (specName, "specialist%ld",t); #ifndef NO_LISP + [dataArchiver putShallow: worldName object: outputWorld]; [dataArchiver sync]; + #else + [dataArchiver putDeep: worldName object: outputWorld]; #endif [dataArchiver putShallow: specName object: outputSpecialist]; #ifndef NO_LISP [archiver sync]; #endif ! // Third, now use the EZGraph dump of its time strings. ! ! if (!hdfWriter) [self initializeHDFWriter]; ! [hdfWriter step]; ! return self; } ! /*"It is necessary to drop the data writing objects in order to make ! sure they finish their work. ! "*/ -(void) drop { ! if (dataOutputFile) fclose(dataOutputFile); ! if (hdfWriter) [hdfWriter drop]; [archiver drop]; [dataArchiver drop]; [super drop]; } + @end + diff -cr ASM-20011125/Parameters.h ASM-2.2/Parameters.h *** ASM-20011125/Parameters.h Tue Oct 23 15:57:48 2001 --- ASM-2.2/Parameters.h Thu Nov 1 07:58:53 2001 *************** *** 6,14 **** @interface Parameters: Arguments_c { ! ASMModelParams * asmModelParams; ! BFParams * bfParams; ! int run; } + createBegin: aZone; --- 6,14 ---- @interface Parameters: Arguments_c { ! ASMModelParams * asmModelParams; /*"parameter object used by ASMModelSwarm"*/ ! BFParams * bfParams;/*"parameter object used by BFagent and its various objects, such as BFCast "*/ ! int run; /*an integer indicating the run number of the current simulation. This is passed in as a command line parameter, as in --run=666 or such."*/ } + createBegin: aZone; diff -cr ASM-20011125/Parameters.m ASM-2.2/Parameters.m *** ASM-20011125/Parameters.m Tue Oct 23 15:57:48 2001 --- ASM-2.2/Parameters.m Thu Nov 1 07:58:53 2001 *************** *** 7,12 **** --- 7,28 ---- @implementation Parameters + /*"The Artificial Stock Market model has a very large set of + parameters. Until ASM-2.2, these paramters were set inside various + implementation files, making them difficult to find/maintain. Now all + parameters are set through separate objects, which can be called upon + whenever needed. + + The Parameters class is an example of a general + purpose Swarm command-line processing class. In case one is designing + batch simulations, this is the place to customize the command line + options and read them into the program. From the command line, type + "./asm --help" to see a list of command line arguments this program + will respond to. You should see all default Swarm command line parameters + as well as --run, which is specific to this project. More parameters can be added in the parseKey:arg: method of this class. + + This class also takes responsibility for making sure that objects to manager parameters for the ASMModelSwarm and the BFagents are created. + "*/ + createBegin: aZone { *************** *** 23,28 **** --- 39,51 ---- } + /*"In order to parse command line parameters, this method runs. + Because Parameters is subclassed from the Swarm Arguments class, + whatever keys we check for in this method will always be checked. + Since processing of command line parameters has not yet been a focal + point, only one parameter, the "run" number, is processed here. + This is included mainly as an example of how other parameters might + be managed."*/ - (int)parseKey: (int) key arg: (const char*) arg { if (key == 'R') *************** *** 36,42 **** } ! - init { if ((asmModelParams = --- 59,69 ---- } ! /*"This performs the vital job of using the lispAppArchiver to read ! the baseline values of the parameters out of the asm.scm file and ! then creating the parameter objects--asmModelParms and ! bfParams--that hold those values and make them avalable to the ! various objects in the model "*/ - init { if ((asmModelParams = *************** *** 53,75 **** return self; } ! - (ASMModelParams*) getModelParams { return asmModelParams; } ! ; - (BFParams*) getBFParams; { return bfParams; } - (int) getRunArg { return run; } ! -sayHello { printf("You are a dirty scoundrel"); --- 80,111 ---- return self; } ! /*"Returns an instance of ASMModelParams, the object which holds the model-level input parameters"*/ - (ASMModelParams*) getModelParams { return asmModelParams; } ! ! ! /*"Returns an instance of the BFParams class, an object which holds ! the default parameter of the BFagents. If they desire to do so, ! BFagents can create their own instances of BFParams, copy default ! settings, and then allow their parameters to 'wander'. (As far as I ! know, this potential did not exist before and has not been ! used. PJ-2001-10-31) "*/ - (BFParams*) getBFParams; { return bfParams; } + + /*"Unless one wants to make all IVARS public and access them with ->, then one should create get methods, one for each argument. This gets the run number."*/ - (int) getRunArg { return run; } ! /*"Sometimes we worry that the Parameter object did not get created properly, so this method tells it to speak to the command line with a warm greeting"*/ -sayHello { printf("You are a dirty scoundrel"); diff -cr ASM-20011125/README ASM-2.2/README *** ASM-20011125/README Wed Oct 24 18:26:08 2001 --- ASM-2.2/README Wed Oct 31 13:58:28 2001 *************** *** 143,148 **** --- 143,164 ---- + ASM-2.2 November 2001 + + This work was mainly aimed at documentation, inserting Autodoc markers so I can build nicer looking documentation. + + The only big substantive cleanup was in ASMModelSwarm. I had been avoiding + this for a long time because the scheduling that was used was very + complicated and hard to understand. I succeeded in a major simplicification + and cleanup and have verified the numerical results are identical. This is + explained in the beginning of ASMModelSwarm.m + + There was also a "procedural" cleanup of the code in World.m. The old + version had a lot of callocs and other memory magic because it was keeping + moving averages "by hand". This has been changed. There is now a MovingAverage + class that is used to do that magic, so the code in World.m is much easier to read. + There is much less low level math floating around. + -------original readme follows This is the port to Swarm of the original NeXTstep version of the Brian Arthur, John Holland, Blake LeBaron, Richard Palmer, and diff -cr ASM-20011125/Specialist.h ASM-2.2/Specialist.h *** ASM-20011125/Specialist.h Wed Oct 24 18:26:08 2001 --- ASM-2.2/Specialist.h Tue Nov 27 21:03:34 2001 *************** *** 13,55 **** @interface Specialist: SwarmObject { ! double maxprice; ! double minprice; ! double eta; ! double etainitial; ! double minexcess; ! double rea; ! double reb; ! double bidfrac; ! double offerfrac; ! int maxiterations; ! id agentList; ! double volume; ! double taupdecay; ! double taupnew; @private ! World * worldForSpec; ! SpecialistType sptype; } // Methods to set parameters ! -setAgentList: (id)aList; ! -setWorld: (World *)myWorld; ! -setMaxPrice: (double)maximumPrice; ! -setMinPrice: (double)minimumPrice; ! -setTaup: (double)aTaup; ! -setSPtype: (int)i; ! -setMaxIterations: (int)someIterations; ! -setMinExcess: (double)minimumExcess; ! -setETA: (double)ETA; ! ! -setREA: (double)REA; ! -setREB: (double)REB; ! ! ! -(double)performTrading; ! -(double)getVolume; ! -completeTrades; @end --- 13,53 ---- @interface Specialist: SwarmObject { ! double maxprice; /*"Ceiling on stock price"*/ ! double minprice; /*"Floor under stock price"*/ ! double eta; /*"Used in adjusting price to balance supply/demand"*/ ! // double etainitial; /not used in ASM-2.0 ! double minexcess; /*"excess demand must be smaller than this if the price adjustment process is to stop"*/ ! double rea; /*"rational expectations benchmark"*/ ! double reb; /*" trialprice = rea*dividend + reb "*/ ! double bidfrac; /*"used in completing trades: volume/bidtotal"*/ ! double offerfrac; /*"used in completing trades: volume/offertotal"*/ ! int maxiterations; /*" maximum passes while adjusting trade conditions"*/ ! // id agentList; /*" set of traders whose demands must be reconciled"*/ ! double volume; /*" volume of trades conducted"*/ ! double taupdecay; /*"The agent's profit is calculated as an exponentially weighted moving average. This coefficient weights old inputs in the EWMA"*/ ! double taupnew; /*"Used in calculating exponentially weighted moving average; taupnew = -expm1(-1.0/aTaup); taupdecay = 1.0 - taupnew; "*/ @private ! // World * worldForSpec; /*" reference to World object that keeps data"*/ ! SpecialistType sptype; /*" an enumerated type indicating the sort of Specialist is being used, valued 0, 1, or 2"*/ } // Methods to set parameters ! - setMaxPrice: (double)maximumPrice; ! - setMinPrice: (double)minimumPrice; ! - setTaup: (double)aTaup; ! - setSPtype: (int)i; ! - setMaxIterations: (int)someIterations; ! - setMinExcess: (double)minimumExcess; ! - setETA: (double)ETA; ! ! - setREA: (double)REA; ! - setREB: (double)REB; ! ! ! - (double)performTrading: (id)agentList Market: (id)worldForSpec; ! - (double)getVolume; ! - completeTrades: agentList Market: worldForSpec; @end diff -cr ASM-20011125/Specialist.m ASM-2.2/Specialist.m *** ASM-20011125/Specialist.m Tue Oct 23 15:57:48 2001 --- ASM-2.2/Specialist.m Tue Nov 27 21:03:34 2001 *************** *** 1,27 **** // The Santa Fe Stockmarket -- Implementation of Specialist class - // One instance of this class is used to manage the trading and - // set the stock price. It also manages the market-level parameters. - - // -init - // Initializes other values besides the parameters. - // - // -(double)performTrading - // This is the core method that sets a succession of trial prices - // and asks the agents for their bids or offer at each, generally - // adjusting the price towards reducing |bids - offers|. - // - // -completeTrades - // Updates the agents cash and position to consummate the trades - // previously negotiated in -performTrading, with rationing if - // necessary. - // - // -(double)getVolume - // Relayed to ASMObserverSwarm for graphical interface (and data output). - // - // double price, dividend, profitperunit - // Global market variables in World. - #import "Specialist.h" #import "BFagent.h" --- 1,5 ---- *************** *** 31,66 **** @implementation Specialist - //All set member functions take parameters from the ASMModelSwarm. - -setAgentList: (id)aList - { - agentList = aList; - return self; - } ! ! -setWorld: (World *)myWorld; ! { ! worldForSpec = myWorld; ! return self; ! } ! -setMaxPrice: (double)maximumPrice { maxprice = maximumPrice; return self; } ! -setMinPrice: (double)minimumPrice { minprice = minimumPrice; return self; } ! -setTaup: (double)aTaup { taupnew = -expm1(-1.0/aTaup); //pj: moved here from init method taupdecay = 1.0 - taupnew; // moved to simplify! --- 9,34 ---- @implementation Specialist ! /*" One instance of this class is used to manage the trading and ! set the stock price. It also manages the market-level parameters."*/ ! - setMaxPrice: (double)maximumPrice { maxprice = maximumPrice; return self; } ! - setMinPrice: (double)minimumPrice { minprice = minimumPrice; return self; } ! - setTaup: (double)aTaup { taupnew = -expm1(-1.0/aTaup); //pj: moved here from init method taupdecay = 1.0 - taupnew; // moved to simplify! *************** *** 68,74 **** } ! -setSPtype: (int)i { if(i != 0 && i != 1 && i != 2) { --- 36,46 ---- } ! ! /*"The specialist can be set to type 0, 1, or 2. If this variable is ! set to any other value, the model will set the Specialist to type 1 ! and give a warning in the terminal"*/ ! - setSPtype: (int)i { if(i != 0 && i != 1 && i != 2) { *************** *** 80,114 **** return self; } ! ! -setMaxIterations: (int)someIterations { maxiterations = someIterations; return self; } ! -setMinExcess: (double)minimumExcess { minexcess = minimumExcess; return self; } ! -setETA: (double)ETA { eta = ETA; return self; } ! -setREA: (double)REA { rea = REA; return self; } ! -setREB: (double)REB { reb = REB; return self; --- 52,86 ---- return self; } ! /*" Set the maximum number of interations to be done while looking for a market clearing price"*/ ! - setMaxIterations: (int)someIterations { maxiterations = someIterations; return self; } ! - setMinExcess: (double)minimumExcess { minexcess = minimumExcess; return self; } ! - setETA: (double)ETA { eta = ETA; return self; } ! - setREA: (double)REA { rea = REA; return self; } ! - setREB: (double)REB { reb = REB; return self; *************** *** 119,136 **** // -init ! -(double)performTrading ! /* ! * Performs the trading, getting bids and offers from the agents and ! * adjusting the price. Returns the final trading price, which becomes ! * the next market price. Various methods are implemented, but all ! * have the structure: ! * 1. Set a trial price ! * 2. Send each agent a -getDemandAndSlope:forPrice: message and ! * accumulate the total number of bids and offers at that price. ! * 3. [In some cases] go to 1. ! * 4. Return the last trial price. ! */ { int mcount; BOOL done; --- 91,111 ---- // -init ! - (double)performTrading: (id) agentList Market: (id) worldForSpec ! /*" This is the core method that sets a succession of trial prices and ! * asks the agents for their bids or offer at each, generally ! * adjusting the price towards reducing |bids - offers|. * It gets ! * bids and offers from the agents and * adjuss the price. Returns ! * the final trading price, which becomes * the next market price. ! * Various methods are implemented, but all * have the structure: ! * 1. Set a trial price ! ! 2. Send each agent a -getDemandAndSlope:forPrice: message and accumulate the total ! * number of bids and offers at that price. ! ! 3. [In some cases] go to 1. ! ! 4. Return the last trial price. "*/ { int mcount; BOOL done; *************** *** 234,257 **** return trialprice; } ! ! -(double)getVolume { return volume; } ! -completeTrades ! /* * Makes the actual trades at the last trial price (which is now the * market price), by adjusting the agents' holdings and cash. The * actual purchase/sale my be less than that requested if rationing ! * is impsed by the specialist -- usually one of "bidfrac" and * "offerfrac" will be less than 1.0. * * This could easiliy be done by the agents themselves, but we let * the specialist do it for efficiency. ! */ { Agent * agent; id index; --- 209,235 ---- return trialprice; } ! /*"Returns the volume of trade to anybody that wants, such as the observer or output objects"*/ ! - (double)getVolume { return volume; } ! - completeTrades: agentList Market: worldForSpec ! /*"Updates the agents cash and position to consummate the trades ! previously negotiated in -performTrading, with rationing if ! necessary. ! * Makes the actual trades at the last trial price (which is now the * market price), by adjusting the agents' holdings and cash. The * actual purchase/sale my be less than that requested if rationing ! * is imposed by the specialist -- usually one of "bidfrac" and * "offerfrac" will be less than 1.0. * * This could easiliy be done by the agents themselves, but we let * the specialist do it for efficiency. ! "*/ { Agent * agent; id index; *************** *** 297,302 **** --- 275,282 ---- + + diff -cr ASM-20011125/World.h ASM-2.2/World.h *** ASM-20011125/World.h Sun May 7 13:09:26 2000 --- ASM-2.2/World.h Tue Nov 6 01:29:38 2001 *************** *** 2,39 **** #import @interface World: SwarmObject { ! double intrate; ! double dividendscale; ! double saveddividend; ! double savedprice; ! int * pupdown; /* array, dimension UPDOWNLOOKBACK */ ! int * dupdown; /* array, dimension UPDOWNLOOKBACK */ ! double * divhistory; /* array, dimension maxhistory */ ! double * pricehistory; /* array, dimension maxhistory */ ! double * aweight; /* array, dimension NMAS */ ! double * bweight; /* array, dimension NMAS */ ! int history_top; ! int updown_top; ! int maxhistory; ! double price; ! double oldprice; ! double dividend; ! double olddividend; ! double riskNeutral; ! double profitperunit; ! double returnratio; ! ! int nmas; ! int * matime; ! double * pmav; ! double * oldpmav; ! double * dmav; ! double * olddmav; ! int nworldbits; ! int * realworld; ! BOOL exponentialMAs; } +(const char *)descriptionOfBit: (int)n; --- 2,55 ---- #import + /*" Macro: Number of up/down movements to store for price and dividend, + including the current values. Used for pup, pup1, + ... pup[UPDOWNLOOKBACK-1], and similarly for dup[n], and for + -pricetrend:. The argument to -pricetrend: must be UPDOWNLOOKBACK + or less. "*/ + #define UPDOWNLOOKBACK 5 + + /*" Macro: Number of moving averages "*/ + #define NMAS 4 + /*" Macro: The longest allowed Moving Average "*/ + #define MAXHISTORY 500 + @interface World: SwarmObject { ! @public ! double intrate; /*" interest rate"*/ ! double dividendscale; /*" The baseline dividend that is set by initWithBaseline: "*/ ! ! int pupdown[UPDOWNLOOKBACK]; /*" array, dimension UPDOWNLOOKBACK "*/ ! int dupdown[UPDOWNLOOKBACK]; /*" array, dimension UPDOWNLOOKBACK "*/ ! ! int history_top; /*" index value of current input into history arrays "*/ ! int updown_top; /*"number of time steps to look back to form pupdown and dupdown bits"*/ ! double price; /*"market clearning price"*/ ! double oldprice; /*" previous price "*/ ! double dividend; /*" dividend "*/ ! double olddividend; /*"previous dividend"*/ ! double saveddividend; /* copy of olddividend, used for some ! double-checking on object integrity"*/ ! double savedprice; /* copy of oldprice, used for some ! double-checking on object integrity"*/ ! double riskNeutral; /*"dividend/intrate"*/; ! double profitperunit; /*"price - oldprice + dividend"*/ ! double returnratio; /*"profitperunit/oldprice"*/ ! ! int malength[NMAS]; /*" For each MA, we must specify the length over which the average is calculated. This array has one integer for each of the moving averages we plan to keep, and it is used to set the widths covered by the moving averages."*/ ! ! int nworldbits; /*"The number of aspects of the world that are recorded as bits"*/ ! int * realworld; /*"An array (dynamically allocated, sorry) of ints, one for each bit being monitored. This is kept up-to-date. There's a lot of pointer math going on with this and I don't feel so glad about it (PJ: 2001-11-01)"*/ ! BOOL exponentialMAs; /*"Indicator variable, YES if the World is supposed to report back exponentially weighted moving averages"*/ ! id priceMA[NMAS]; /*" MovingAverage objects which hold price information. There are NMAS of these, and have various widths for the moving averages "*/ ! id divMA[NMAS]; /*" MovingAverage objects which hold dividend moving averages. "*/ ! id oldpriceMA[NMAS]; /*" MovingAverage objects which hold lagged price moving averages "*/ ! id olddivMA[NMAS];/*" MovingAverage objects which hold lagged dividend moving averages "*/ ! @private ! double * divhistory; /*" dividend history array, goes back MAXHISTORY points"*/ ! double * pricehistory; /*" price history array "*/ ! } +(const char *)descriptionOfBit: (int)n; diff -cr ASM-20011125/World.m ASM-2.2/World.m *** ASM-20011125/World.m Wed Oct 24 18:26:08 2001 --- ASM-2.2/World.m Tue Nov 6 01:29:38 2001 *************** *** 1,128 **** // The Santa Fe Stockmarket -- Implementation of class World - // One instance of this object is created to manage the "world" variables - // -- the globally-visible variables that reflect the market itself, - // including the moving averages and the world bits. Everything is based - // on just two basic variables, price and dividend, which are set only - // by the -setPrice: and -setDividend: messages. - // - // The World also manages the list of bits, translating between bit names - // and bit numbers, and providing descriptions of the bits' functions. - // These are done by class methods because they are needed before the - // instnce is instantiated. - - // PUBLIC METHODS - // - // +(const char *)descriptionOfBit: (int)n - // Supplies a description of the specified bit, taken from the - // bitnamelist[] table below. Also works for NULLBIT. - // - // +(const char *)nameOfBit: (int)n - // Supplies the name of the specified bit, taken from the - // bitnamelist[] table below. Also works for NULLBIT. - // - // +(int)bitNumberOf: (const char *)name - // Supplies the number of a bit given its name. Unknown names - // return NULLBIT. Relatively slow (linear search). - // - // +writeNamesToFile: (FILE *)fp - // Writes the name and description of all the bits to file "fp". - // - // -setintrate: (double)rate - // Interest rate set in ASMModelSwarm. - // - // -setExponentialMAs: (BOOL)aBOOL - // Established in ASMModelSwarm. Whether to use exponenetially weighted - // moving averages, or (if NO) simple averages of the last N periods. - // - // -(int)getNumWorldBits - // Returns numworldbits; used by teh BFagent. - // - // -initWithBaseline: (double)base - // Initializes the World instance, using initial values based on - // a price scale of "baseline" for the dividend. The baseline is - // set in ASMModelSwarm. - // - // -setPrice: (double)p - // Sets the market price to "p". All price changes (besides trial - // prices) should use this method. Also computes profitperunit and - // returnratio. - // - // -(double)getPrice - // Returns the price, used by many. - // - // -(double)getProfitperUnit - // Returns profitperunit, used by Specialist. - // - // -setDividend: (double)d - // Sets the dividend to "d". All dividend changes should use - // this method. - // - // -(double)getDividend - // Returns the dividend, used by many. - // - // -(double)getRiskNeutral - // Returns the risk neutral price. It is just dividend/intrate. - // - // -updateWorld - // Updates all the other World variables (moving averages and bits) - // on the basis of the most recent -setPrice: and -setDividend: messages. - // Called once per period. [This could be done automatically as - // part of -setDividend:]. - // - // -getRealWorld: (int *)anArray - // Returns the real world string of bits. Used by BFagent to compare - // their worlds to the real world. - // - // -(int)pricetrend: (int)nperiods - // Returns 1 or -1 respectively if the price has risen or fallen - // monotonically at the last "nperiods". Otherwise returns 0. Causes - // an error if nperiods is too large (see UPDOWNLOOKBACK). - // // SOME VARIABLES EXPLAINED - // int nmas - // Number of moving averages available. This is the dimension of - // the following moving average arrays. // ! // int matime[] // List of moving average periods. // - // double pmav[] - // The current moving averages of price. E.g. for period = 5, this - // is the average of the price 1, 2, 3, 4 and 5 periods ago. - // - // double oldpmav[] - // The previous moving averages of price; e.g. for period = 5, this - // is the average of the price 6, 7, 8, 9, and 10 periods ago. - // - // double dmav[] - // The current moving averages of dividend. E.g. for period = 5, this - // is the average of the dividend 0, 1, 2, 3, and 4 periods ago. - // - // double olddmav[] - // The previous moving averages of dividend; e.g. for period = 5, this - // is the average of the dividend 5, 6, 7, 8, and 9 periods ago. #import "World.h" ! #import #include #include - // List of bit names and descriptions - // NB: If you change the order or meaning of bits, also check or change: - // 1. Their computation in -makebitvector in this file. - // 2. The PUPDOWBITNUM value. - // 3. The NAMES documentation file -- do "market -n > NAMES". //pj: ! // These are convenience macros to replace stuff from ASM random with Swarm random stuff #define drand() [uniformDblRand getDoubleSample] #define urand() [uniformDblRand getDoubleWithMin: -1 withMax: 1] #define irand(x) [uniformIntRand getIntegerWithMin: 0 withMax: x-1] static struct bitnamestruct { const char *name; --- 1,38 ---- // The Santa Fe Stockmarket -- Implementation of class World // SOME VARIABLES EXPLAINED // ! // int malength[] // List of moving average periods. // #import "World.h" ! #import "MovingAverage.h" ! ! #import #include #include //pj: ! /*" drand(), urand() and irand(x) are convenience macros to replace stuff from ASM random with Swarm random stuff "*/ #define drand() [uniformDblRand getDoubleSample] #define urand() [uniformDblRand getDoubleWithMin: -1 withMax: 1] #define irand(x) [uniformIntRand getIntegerWithMin: 0 withMax: x-1] + /*" GETMA(x,j) is a macro that checks to see if we want an exponential MA or regular when we retrieve values from MA objects "*/ + #define GETMA(x,j) exponentialMAs ? [x[j] getEWMA]:[x[j] getMA] + + /*" bitname struct holds the substantive information about various world indicators + It is a list of bit names and descriptions + // NB: If you change the order or meaning of bits, also check or change: + // 1. Their computation in -makebitvector in this file. + // 2. The PUPDOWBITNUM value. + // 3. The NAMES documentation file -- do "market -n > NAMES". + "*/ static struct bitnamestruct { const char *name; *************** *** 209,223 **** // The index of the "pup" bit #define PUPDOWNBITNUM 42 - // Number of moving averages - #define NMAS 4 - - // Number of up/down movements to store for price and dividend, including - // the current values. Used for pup, pup1, ... pup[UPDOWNLOOKBACK-1], and - // similarly for dup[n], and for -pricetrend:. The argument to -pricetrend: - // must be UPDOWNLOOKBACK or less. - #define UPDOWNLOOKBACK 5 - // Breakpoints for price*interest/dividend and dividend/mean-dividend ratios. static double ratios[] = {0.25, 0.5, 0.75, 0.875, 1.0, 1.125, 1.25, 1.5, 2.0, 4.0}; --- 119,124 ---- *************** *** 227,239 **** // ------ Private methods ------ @interface World(Private) -makebitvector; @end @implementation World ! +(const char *)descriptionOfBit: (int)n { if (n == NULLBIT) return "(Unused bit for spacing)"; --- 128,158 ---- // ------ Private methods ------ @interface World(Private) + -makebitvector; + @end @implementation World ! /*" The World is a class that is mainly used to serve the information needs of BFagents. The WOrld takes price data and converts it into a number of trends, averages, and so forth. ! ! One instance of this object is created to manage the "world" variables ! -- the globally-visible variables that reflect the market itself, ! including the moving averages and the world bits. Everything is based ! on just two basic variables, price and dividend, which are set only by ! the -setPrice: and -setDividend: messages. ! ! The World also manages the list of bits, translating between bit names ! and bit numbers, and providing descriptions of the bits' functions. ! These are done by class methods because they are needed before the ! instnce is instantiated."*/ ! ! /*" Supplies a description of the specified bit, taken from the ! * bitnamelist[] table below. Also works for NULLBIT. ! "*/ ! + (const char *)descriptionOfBit: (int)n { if (n == NULLBIT) return "(Unused bit for spacing)"; *************** *** 243,252 **** } ! +(const char *)nameOfBit: (int)n ! /* ! * Converts a bit number to a bit name. ! */ { if (n == NULLBIT) return "null"; --- 162,173 ---- } ! ! /*" Supplies the name of the specified bit, taken from the ! // bitnamelist[] table below. Also works for NULLBIT. Basically, ! // it converts a bit number to a bit name. ! "*/ ! + (const char *)nameOfBit: (int)n { if (n == NULLBIT) return "null"; *************** *** 256,266 **** } ! +(int)bitNumberOf: (const char *)name ! /* ! * Converts a bit name to a bit number. Could be made faster with ! * a hash table etc, but that's not worth it for the intended usage. ! */ { int n; --- 177,187 ---- } ! + (int)bitNumberOf: (const char *)name ! /*" Converts a bit name to a bit number. Supplies the number of a bit ! * given its name. Unknown names return NULLBIT. Relatively slow ! * (linear search). Could be made faster with a hash table etc, but ! * that's not worth it for the intended usage. "*/ { int n; *************** *** 273,302 **** } ! -setintrate: (double)rate { intrate = rate; return self; } ! -setExponentialMAs: (BOOL)aBool { exponentialMAs = aBool; return self; } ! -(int)getNumWorldBits { return nworldbits; } ! -initWithBaseline: (double)baseline ! /* ! * Initializes arrays etc. Returns maxhistory. ! */ { int i; double initprice, initdividend; --- 194,229 ---- } ! - setintrate: (double)rate ! /*" Interest rate set in ASMModelSwarm."*/ { intrate = rate; return self; } ! - setExponentialMAs: (BOOL)aBool ! /*" Turns on the use of exponential MAs in calculations. Can be ! turned on in GUI or ASMModelSwarm.m. If not, simple averages of ! the last N periods."*/ { exponentialMAs = aBool; return self; } ! - (int)getNumWorldBits ! /*" Returns numworldbits; used by the BFagent."*/ { return nworldbits; } ! - initWithBaseline: (double)baseline ! /*" ! Initializes moving averages, using initial values based on ! a price scale of "baseline" for the dividend. The baseline is ! set in ASMModelSwarm. " */ { int i; double initprice, initdividend; *************** *** 320,385 **** // Initialize miscellaneous variables nworldbits = NWORLDBITS; ! nmas = NMAS; ! ! matime = calloc(NMAS, sizeof(int)); ! if(!matime) ! printf("Error allocating memory for matime."); ! matime[0] = 5; ! matime[1] = 20; ! matime[2] = 100; ! matime[3] = 500; history_top = 0; updown_top = 0; - if (exponentialMAs) - maxhistory = matime[NMAS-1]; - else - maxhistory = 2*matime[NMAS-1]; - - // Allocate arrays - pupdown = calloc(UPDOWNLOOKBACK,sizeof(int)); - if(!pupdown) - printf("Error allocating memory for pupdown."); - - dupdown = calloc(UPDOWNLOOKBACK,sizeof(int)); - if(!dupdown) - printf("Error allocating memory for dupdown."); ! pricehistory = calloc(maxhistory,sizeof(double)); ! if(!pricehistory) ! printf("Error allocating memory for pricehistory."); ! ! divhistory = calloc(maxhistory,sizeof(double)); ! if(!divhistory) ! printf("Error allocating memory for divhistory."); ! ! if (exponentialMAs) ! { ! aweight = calloc(NMAS,sizeof(double)); ! if(!aweight) ! printf("Error allocating memory for aweight."); ! bweight = calloc(NMAS,sizeof(double)); ! if(!bweight) ! printf("Error allocating memory for bweight."); ! } - pmav = calloc(NMAS,sizeof(double)); - if(!pmav) - printf("Error allocating memory for pmav."); - - oldpmav = calloc(NMAS, sizeof(double)); - if(!oldpmav) - printf("Error allocating memory for oldpmav."); - - dmav = calloc(NMAS, sizeof(double)); - if(!dmav) - printf("Error allocating memory for dmav."); - - olddmav = calloc(NMAS, sizeof(double)); - if(!olddmav) - printf("Error allocating memory for olddmav."); - realworld = calloc(NWORLDBITS, sizeof(int)); if(!realworld) printf("Error allocating memory for realworld."); --- 247,264 ---- // Initialize miscellaneous variables nworldbits = NWORLDBITS; ! ! malength[0] = 5; ! malength[1] = 20; ! malength[2] = 100; ! malength[3] = MAXHISTORY; history_top = 0; updown_top = 0; ! divhistory = [[self getZone] alloc: MAXHISTORY*sizeof(double)]; ! pricehistory = [[self getZone] alloc: MAXHISTORY*sizeof(double)]; realworld = calloc(NWORLDBITS, sizeof(int)); if(!realworld) printf("Error allocating memory for realworld."); *************** *** 391,397 **** dupdown[i] = 0; } ! for (i = 0; i < maxhistory; i++) { pricehistory[i] = initprice; divhistory[i] = initdividend; --- 270,276 ---- dupdown[i] = 0; } ! for (i = 0; i < MAXHISTORY; i++) { pricehistory[i] = initprice; divhistory[i] = initdividend; *************** *** 399,417 **** for (i = 0; i < NMAS; i++) { ! pmav[i] = initprice; ! oldpmav[i] = initprice; ! dmav[i] = initdividend; ! olddmav[i] = initdividend; ! } ! if (exponentialMAs) ! { ! for (i = 0; i < NMAS; i++) ! { ! bweight[i] = -expm1(-1.0/matime[i]); ! aweight[i] = 1.0 - bweight[i]; ! } } // Initialize bits --- 278,294 ---- for (i = 0; i < NMAS; i++) { ! priceMA[i] = [MovingAverage create: [self getZone]]; ! [priceMA[i] initWidth: malength[i] Value: initprice]; ! ! divMA[i] = [MovingAverage create: [self getZone]]; ! [divMA[i] initWidth: malength[i] Value: initdividend]; ! oldpriceMA[i] = [MovingAverage create: [self getZone]]; ! [oldpriceMA[i] initWidth: malength[i] Value: initprice]; ! ! olddivMA[i] = [MovingAverage create: [self getZone]]; ! [olddivMA[i] initWidth: malength[i] Value: initdividend]; } // Initialize bits *************** *** 420,435 **** return self; } ! /* ! * Sets the current price to p. Also computes profitperunit and ! * returnration. ! * Checks internally for illegal changes of "price", giving us the ! * effective benefit of encapsulation with the simplicity of use of ! * a global variable. ! */ ! ! -setPrice: (double)p ! { if (price != savedprice) printf("Price was changed illegally"); --- 297,308 ---- return self; } ! /*" Sets the market price to "p". All price changes (besides trial ! prices) should use this method. Also computes profitperunit and ! returnratio. Checks internally for illegal changes of "price", giving us the ! effective benefit of encapsulation with the simplicity of use of ! a global variable. "*/ ! - setPrice: (double)p { if (price != savedprice) printf("Price was changed illegally"); *************** *** 448,471 **** return self; } ! ! -(double)getPrice { return price; } ! ! -(double)getProfitPerUnit { return profitperunit; } ! -setDividend: (double)d ! /* ! * Sets the global value of "dividend". Checks for illegal changes, like ! * -setPrice:. ! */ { if (dividend != saveddividend) printf("Dividend was changed illegally."); --- 321,343 ---- return self; } ! /*"Returns the price, used by many classes."*/ ! - (double)getPrice { return price; } ! /*"Returns profitperunit, used by Specialist."*/ ! - (double)getProfitPerUnit { return profitperunit; } ! /*"Sets the global value of "dividend". All dividend changes should ! use this method. It checks for illegal changes, as does ! -setPrice:."*/ ! - setDividend: (double)d { if (dividend != saveddividend) printf("Dividend was changed illegally."); *************** *** 479,516 **** return self; } ! ! -(double)getDividend { return dividend; } ! ! -(double)getRiskNeutral { return riskNeutral; } ! /* ! * Updates the history records, moving averages, and world bits to * reflect the current price and dividend. Note that this is called * in each period after a new dividend has been declared but before ! * the bidding and price adjustment. The bits seen by the agents ! * thus do NOT reflect the trial price. The "price" here becomes ! * the "oldprice" by the end of the period. * * The dividend used here is at present the latest value, though it * could be argued that it should be the one before, to match price. * For the p*r/d bits we do use the old one. ! */ ! -updateWorld { register int i; - int r, rago; - double m; - int rrago; /* Update the binary up/down indicators for price and dividend */ updown_top = (updown_top + 1) % UPDOWNLOOKBACK; --- 351,385 ---- return self; } ! /*"Returns the most recent dividend, used by many."*/ ! - (double)getDividend { return dividend; } ! /*"Returns the risk neutral price. It is just dividend/intrate."*/ ! - (double)getRiskNeutral { return riskNeutral; } ! /*" Updates the history records, moving averages, and world bits to * reflect the current price and dividend. Note that this is called * in each period after a new dividend has been declared but before ! * the bidding and price adjustment. The bits seen by the agents thus ! * do NOT reflect the trial price. The "price" here becomes the ! * "oldprice" by the end of the period. It is called once per period. ! * (This could be done automatically as part of -setDividend:). * * The dividend used here is at present the latest value, though it * could be argued that it should be the one before, to match price. * For the p*r/d bits we do use the old one. ! "*/ ! - updateWorld { register int i; /* Update the binary up/down indicators for price and dividend */ updown_top = (updown_top + 1) % UPDOWNLOOKBACK; *************** *** 518,553 **** dupdown[updown_top] = dividend > olddividend; /* Update the price and dividend moving averages */ ! history_top = history_top + 1 + maxhistory; ! if (exponentialMAs) ! { ! for (i = 0; i < NMAS; i++) ! { ! r = matime[i]; ! rago = (history_top-r)%maxhistory; ! pmav[i] = aweight[i]*pmav[i] + bweight[i]*price; ! oldpmav[i] = aweight[i]*oldpmav[i] + bweight[i]*pricehistory[rago]; ! dmav[i] = aweight[i]*dmav[i] + bweight[i]*dividend; ! olddmav[i] = aweight[i]*olddmav[i] + bweight[i]*divhistory[rago]; ! } ! } ! else { ! for (i = 0; i < NMAS; i++) ! { ! r = matime[i]; ! m = 1.0 / (double)r; ! rago = (history_top-r)%maxhistory; ! rrago = (history_top-r-r)%maxhistory; ! pmav[i] += (price - pricehistory[rago]) * m; ! oldpmav[i] += (pricehistory[rago] - pricehistory[rrago]) * m; ! dmav[i] += (dividend - divhistory[rago]) * m; ! olddmav[i] += (divhistory[rago] - divhistory[rrago]) * m; ! } } /* Update the price and dividend histories */ ! history_top %= maxhistory; pricehistory[history_top] = price; divhistory[history_top] = dividend; --- 387,410 ---- dupdown[updown_top] = dividend > olddividend; /* Update the price and dividend moving averages */ ! history_top = history_top + 1 + MAXHISTORY; ! ! //update moving averages of price and dividend ! ! for (i = 0; i < NMAS; i++) { ! int rago = (history_top-malength[i])%MAXHISTORY; ! ! [priceMA[i] addValue: price]; ! [divMA[i] addValue: dividend]; ! ! [oldpriceMA[i] addValue: pricehistory[rago]]; ! [olddivMA[i] addValue: divhistory[rago]]; } + /* Update the price and dividend histories */ ! history_top %= MAXHISTORY; pricehistory[history_top] = price; divhistory[history_top] = dividend; *************** *** 558,575 **** } ! -makebitvector ! /* ! * Set all the world bits, based on the current dividend, price, and ! * their moving averages and histories. ! */ { register int i, j, k, temp; double multiple; ! // Note that "i" increases monotonically throughout this routine, always ! // being the next bit to assign. It is crucial that the order here is the ! // same as in bitnamelist[]. i = 0; realworld[i++] = 1; --- 415,432 ---- } ! - makebitvector ! /*" Set all the world bits, based on the current dividend, price, ! and their moving averages and histories. This moves through the ! realworld array, bit by bit, setting the values to 0, 1 or 2, ! according to the data that has been observed. Note the pointer math, such as realworld[i++], that steps the integer i through the array. Note that "i" increases monotonically throughout this routine, always ! being the next bit to assign. It is crucial that the order here is the ! same as in bitnamelist[]. "*/ { register int i, j, k, temp; double multiple; ! i = 0; realworld[i++] = 1; *************** *** 583,598 **** /* Dividend moving averages went up or down */ for (j = 0; j < NMAS; j++) ! realworld[i++] = dmav[j] > olddmav[j]; /* Dividend > MA[j] */ for (j = 0; j < NMAS; j++) ! realworld[i++] = dividend > dmav[j]; /* Dividend MA[j] > dividend MA[k] */ for (j = 0; j < NMAS-1; j++) for (k = j+1; k < NMAS; k++) ! realworld[i++] = dmav[j] > dmav[k]; /* Dividend as multiple of meandividend */ multiple = dividend/dividendscale; --- 440,458 ---- /* Dividend moving averages went up or down */ for (j = 0; j < NMAS; j++) ! realworld[i++] = GETMA(divMA,j) > GETMA(olddivMA,j); ! /* Dividend > MA[j] */ for (j = 0; j < NMAS; j++) ! realworld[i++] = dividend > GETMA(divMA,j); /* Dividend MA[j] > dividend MA[k] */ for (j = 0; j < NMAS-1; j++) for (k = j+1; k < NMAS; k++) ! realworld[i++] = GETMA(divMA,j) > GETMA(divMA,k); ! // realworld[i++] = exponentialMAs ? [divMA[j] getEWMA]:[divMA[j] getMA] > exponentialMAs ? [divMA[k] getEWMA]:[divMA[k] getMA]; ! //realworld[i++] = dmav[j] > dmav[k]; /* Dividend as multiple of meandividend */ multiple = dividend/dividendscale; *************** *** 612,628 **** /* Price moving averages went up or down */ for (j = 0; j < NMAS; j++) ! realworld[i++] = pmav[j] > oldpmav[j]; /* Price > MA[j] */ for (j = 0; j < NMAS; j++) ! realworld[i++] = price > pmav[j]; /* Price MA[j] > price MA[k] */ for (j = 0; j < NMAS-1; j++) for (k = j+1; k < NMAS; k++) ! realworld[i++] = pmav[j] > pmav[k]; ! // Check if (i != NWORLDBITS) printf("Bits calculated != bits defined."); --- 472,491 ---- /* Price moving averages went up or down */ for (j = 0; j < NMAS; j++) ! realworld[i++] = GETMA(priceMA,j) > GETMA(oldpriceMA,j); ! //realworld[i++] =pmav[j] > oldpmav[j]; /* Price > MA[j] */ for (j = 0; j < NMAS; j++) ! realworld[i++] = price > GETMA(priceMA,j); ! // realworld[i++] = price > exponentialMAs ? [priceMA[j] getEWMA]:[priceMA[j] getMA]; ! // realworld[i++] = price > pmav[j]; /* Price MA[j] > price MA[k] */ for (j = 0; j < NMAS-1; j++) for (k = j+1; k < NMAS; k++) ! realworld[i++] = GETMA(priceMA,j) > GETMA(priceMA,k); ! // Check if (i != NWORLDBITS) printf("Bits calculated != bits defined."); *************** *** 638,657 **** return self; } ! ! -getRealWorld: (int *)anArray { memcpy(anArray, realworld, NWORLDBITS*sizeof(int)); return self; } - ! -(int)pricetrend: (int)n; ! /* * Returns +1, -1, or 0 according to whether the price has risen * monotonically, fallen monotonically, or neither, at the last ! * n updates. ! */ { int trend, i; --- 501,522 ---- return self; } ! /*" Returns the real world array of bits. Used by BFagent to compare ! their worlds to the real world."*/ ! - getRealWorld: (int *)anArray { memcpy(anArray, realworld, NWORLDBITS*sizeof(int)); return self; } ! ! - (int)pricetrend: (int)n; ! /*" * Returns +1, -1, or 0 according to whether the price has risen * monotonically, fallen monotonically, or neither, at the last ! * n updates. Causes ! * an error if nperiods is too large (see UPDOWNLOOKBACK)." ! "*/ { int trend, i; diff -cr ASM-20011125/asm.scm ASM-2.2/asm.scm *** ASM-20011125/asm.scm Thu Feb 8 17:09:07 2001 --- ASM-2.2/asm.scm Sat Nov 3 13:55:36 2001 *************** *** 34,40 **** #:nhood 0.05D0 #:genfrac 0.10D0 #:nnulls 0 - #:lastgatime 1 #:condbits 16 #:npoolmax -1 #:nnewmax -1 --- 34,39 ---- *************** *** 73,115 **** #:exponentialMAs 1 )) - - (cons 'asmBatchParams - (make-instance 'ASMModelParams - #:numBFagents 30 - #:initholding 1F0 - #:initialcash 10000D0 - #:minholding 0D0 - #:mincash 0D0 - #:intrate 0.1D0 - #:baseline 10D0 - #:mindividend 0.00005D0 - #:maxdividend 100D0 - #:amplitude 0.14178D0 - #:period 1.0 - #:maxprice 500D0 - #:minprice 0.001D0 - #:taup 50.0D0 - #:sptype 2 - #:maxiterations 10 - #:minexcess 0.01 - #:eta 0.0005D0 - #:etamax 0.05D0 - #:etamin 0.00001D0 - #:rea 9.0D0 - #:reb 2.0D0 - #:randomSeed 0 - #:tauv 50.0D0 - #:lambda 0.3D0 - #:maxbid 10.0D0 - #:initvar 0.4000212D0 - #:maxdev 100D0 - #:exponentialMAs 1 - )) - (cons 'asmBatchSwarm (make-instance 'ASMBatchSwarm ! #:loggingFrequency 1 #:experimentDuration 500 )) ) --- 72,80 ---- #:exponentialMAs 1 )) (cons 'asmBatchSwarm (make-instance 'ASMBatchSwarm ! #:loggingFrequency 3 #:experimentDuration 500 )) ) diff -cr ASM-20011125/batch.setup ASM-2.2/batch.setup *** ASM-20011125/batch.setup Mon Apr 24 20:25:06 2000 --- ASM-2.2/batch.setup Tue Dec 4 14:53:58 2001 *************** *** 1,4 **** @begin loggingFrequency 1 experimentDuration 500 ! @end \ No newline at end of file --- 1,5 ---- @begin loggingFrequency 1 experimentDuration 500 ! @end ! Only in ASM-2.2: doc diff -cr ASM-20011125/main.m ASM-2.2/main.m *** ASM-20011125/main.m Wed Oct 24 18:26:08 2001 --- ASM-2.2/main.m Thu Nov 1 17:55:52 2001 *************** *** 23,30 **** if(swarmGUIMode == 1) theTopLevelSwarm = [ASMObserverSwarm create: globalZone]; else - // theTopLevelSwarm = [ASMBatchSwarm create: globalZone]; - if ((theTopLevelSwarm = [lispAppArchiver getWithZone: globalZone key: "asmBatchSwarm"]) == nil) raiseEvent(InvalidOperation, --- 23,28 ----