Model 1: A Propositional Model of Medical Diagnosis Overview Model 2: A First-Order Model of Medical Diagnosis

Model 1a: Elaborating the Experimental Environment

Encapsulating the subject and the experimental environment

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:

  1. In the Research programme manager, create a new model by right-clicking on Model 1 and selecting Create.... Open the new model and label it "Model 1a".
  2. Add two compound boxes to the main box/arrow diagram.
  3. Link them with send arrows in both directions.
  4. Open each box, and label them Subject and Experimental Environment respectively.
  5. Delete the arrows connecting Experimenter and Input-Output. At this point the diagram should look something like this:

  1. Delete the boxes comprising the Subject
  2. Open the Subject compound box, and paste the subject boxes into it.
  3. Still in the Subject compound, you will notice that the Experimental environment label appears outside the boundary line. Draw send arrows between this and Input-Output, in both directions. At this point the diagram of the Subject model should look something like this:

  1. Back in the root compound, delete the boxes comprising the experimenter subsystem, open the Experimental environment box, and paste the experimenter boxes into it. Again, you must create send arrows linking the Experimenter process box with the Subject label outside the boundary, in both directions. It should look like this:

  1. Close the Experimental environment box. The root compound box should now look like this:

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.

Collecting subject responses

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.

Calculating response statistics

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?

Using tables to present response data

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.


Model 1: A Propositional Model of Medical Diagnosis Overview Model 2: A First-Order Model of Medical Diagnosis