The box/arrow diagram for the propositional Medical Diagnosis model is already quite complicated, and does not explicitly distinguish the Subject from the Experimental Environment. COGENT provides facilities to create compound boxes, which we can use to separately encapsulate the Subject and the Experimental Environment. This is fairly straightforward, since it can be done by deleting the boxes comprising each subsystem from the root compound, and pasting each set back into its own compound box:
Now, you can test the encapsulated system by running it. If it doesn't work, first ensure that all the arrows are properly drawn, then check the rules in the Experimenter process and Input-Output process, to ensure that the send actions have the correct targets.
To conduct computational experiments with our model we need facilities to collect subject responses over a series of trials. The first step is to create a new buffer, called statistics, to which the Experimenter process has both read and write access, as in the figure below:
Since this buffer is used to accumulate responses over a series of trials, it should not be initialised on each trial. Change to the statistics buffer's Properties view and set the Initialise property to Each Block:
Next, you should add the following rule to the Experimenter process:
Rule (refracted): On receiving diagnosis, make record of trial TRIGGER: say(diagnosis_is(Diagnosis)) IF: setup_for(TargetDisease, Presenting) is in Trial Setup Queries is the list of all told(N, Symptom, Value) such that told(N, Symptom, Value) is in Trial Setup the current trial is ThisTrial of _ THEN: add trial(ThisTrial, TargetDisease, Presenting, Queries, Diagnosis) to statistics
The function of this rule is to create a record of the entire trial in the statistics buffer, whenever a diagnosis is received. Try running a block of 10 trials, with the statistics buffer's Current Contents visible; you should see a new trial/5 element appear at the end of each trial, and these should accumulate during the block of trials.
By the way, by this point you may have noticed that the Subject submodel does not always reach a diagnosis, with the consequence that some trials do not create a record. You might like to consider how the rules in the Decision Procedure could be amended to ensure a diagnosis on each trial.
Having created a record of Subject responses, we are now in a position to calculate statistics. One obvious thing to do is to score the number of correct and incorrect responses. The easiest way to do this is to amend the previous rule so that as well as recording trial data, it also triggers rules on the next cycle. First, it will be necessary to set the Experimenter process' Recurrent property to Yes, that is, to allow the process to send messages to itself. Then, add a final action to the diagnosis rule like this:
Rule (refracted): On receiving diagnosis, make record of trial TRIGGER: say(diagnosis_is(Diagnosis)) IF: setup_for(TargetDisease, Presenting) is in Trial Setup Queries is the list of all told(N, Symptom, Value) such that told(N, Symptom, Value) is in Trial Setup the current trial is ThisTrial of _ THEN: add trial(ThisTrial, TargetDisease, Presenting, Queries, Diagnosis) to statistics send calculate_statistics to Experimenter
Now, we can add another rule to calculate the current score:
To make this work, we must also add an element to the Initial Contents of the statistics buffer, like this:
Element: Initial value for scoring score(0/0)
Now, we add the following two rules. These update the score respectively, when the diagnosis is correct and when it is incorrect. Note the use of the built-in condition current_trial, whose arguments are the current and total trial numbers.
Rule (unrefracted): Calculate score (correct diagnosis) TRIGGER: calculate_statistics IF: the current trial is Trial of _ trial(Trial, Target, _, _, Target) is in statistics score(Correct / Total) is in statistics Correct1 is (Correct) + (1) THEN: delete score(Correct / Total) from statistics add score(Correct1 / Trial) to statistics send score(Correct1 / Trial) to Experiment Record Rule (unrefracted): Calculate score (incorrect diagnosis) TRIGGER: calculate_statistics IF: the current trial is Trial of _ trial(Trial, Target, _, _, Diagnosis) is in statistics Target \== Diagnosis score(Correct / Total) is in statistics THEN: delete score(Correct / Total) from statistics add score(Correct / Trial) to statistics send score(Correct / Trial) to Experiment Record
Now the model keeps a running score in the statistics buffer, and outputs this to the Experiment Record whenever a diagnosis is received. You might like to think about how the mean number of queries could be calculated from the contents of the statistics buffer. Also, how could you delay calculating the statistics until the end of the block?
Finally in this section, we use a Tabular sink to tabulate Diagnoses against Diseases in a contingency table. First of all, you should add a Tabular sink box to the Experimental environment, and draw a send arrow from the Experimenter process to it, like this:
Next, switch to the Properties view of the new Tabular sink, and set its Initialisation property to Each Block. You will see that you have the opportunity to specify the row and column labels here. It should look something like this:
Next, open the Experimenter process and add the following Condition definition and Rule:
Condition Definition: possible_disease/1: Define possible diseases possible_disease(Disease):- Disease is a member of [laryngitis, parotitis, meningitis, tonsillitis, hepatitis]. Rule (unrefracted): Tabulate Diagnoses against Diseases TRIGGER: calculate_statistics IF: possible_disease(Disease) possible_disease(Diagnosis) Outcomes is the list of all (Disease , Diagnosis) such that trial(_, Disease, _, _, Diagnosis) is in statistics Value is the length of Outcomes THEN: send data(Disease, Diagnosis, Value) to Disease/Diagnosis table
Now, run a block of trials, with the Disease/Diagnosis table sink's Tabular view visible. After the first trial, you should see a tabular representation of the number of diagnoses of each type for each possible disease, which is updated on receipt of each subsequent diagnosis.
As a matter of interest, you can also try the following: add another condition to the rule, after the others, like this:
Value > 0
This blocks any zero values from being added to the table. Run the model again; you will see that even without all cells defined, the defined parts of the table are still drawn.