Maths with QTI – Response Processing

From my experience, the responseProcessing section of a QTI XML file is the most complicated. It is the place where you check whether the user has given the correct response, deal with incorrect responses, and display the appropriate feedback. In theory it could be very simple, if you are just checking whether an answer is correct, showing the ‘correct’ feedback if it is and showing ‘incorrect’ feedback if it is not. However, it is possible to give much more tailored feedback than this, pointing users in the right direction if they have made a common mistake.

Another common use of the responseProcessing is to show a hint and/or a solution if the user requests it. For a generic structure for doing this, please see the Solution and Hint post.

responseProcessing Start and End

<responseProcessing>
<!-- Response processing goes here -->
</responseProcessing>

responseConditions

General Structure

This is the general structure for if…else if…else statements for dealing with responses. responseCondition statements can be nested within each other to create complex hierarchies of conditions.

<responseCondition>
   <responseIf>
   </responseIf>
   <responseElseIf>
   </responseElseIf>
   <responseElse>
   </responseElse>
</responseCondition>

Simple Example

In this example, the RESPONSE is tested for being NULL, in which case a score of 0 is given, and no feedback shown, so the user can just have another go. Next, RESPONSE is tested for being equal to ANSWER (i.e. the response is correct), in which case the CORRECT feedback is shown and the score is set to 1. Otherwise, the INCORRECT feedback is shown and the score set to 0.

<responseCondition>
   <!-- Is the response NULL? -->
   <responseIf>
      <isNull>
         <variable identifier="RESPONSE"/>
      </isNull>
      <setOutcomeValue identifier="SCORE">
         <baseValue baseType="float">0.0</baseValue>
      </setOutcomeValue>
   </responseIf>
   <responseElseIf>
      <!-- Is the response Correct? -->
      <equal toleranceMode="exact">
         <variable identifier="iAnswer"/>
         <variable identifier="RESPONSE"/>
      </equal>
      <setOutcomeValue identifier="FEEDBACK">
         <multiple>
            <baseValue baseType="identifier">CORRECT</baseValue>
         </multiple>
      <setOutcomeValue identifier="SCORE">
         <baseValue baseType="float">1.0</baseValue>
      </setOutcomeValue>
   </responseElseIf>
   <responseElse>
      <!-- Otherwise the response must be incorrect -->
      <setOutcomeValue identifier="FEEDBACK">
         <multiple>
            <baseValue baseType="identifier">INCORRECT</baseValue>
         </multiple>
      </setOutcomeValue>
      <setOutcomeValue identifier="SCORE">
         <baseValue baseType="float">0.0</baseValue>
      </setOutcomeValue>
   </responseElse>
</setOutcomeValue>

Feedback

Empty the Feedback Variable

This empties the feedback ‘multiple’ variable, so any previously shown feedback (from previous response) is removed before we process the current response. The EMPTY variable must have been declared in the Variable Declarations.

<setOutcomeValue identifier="FEEDBACK">
   <multiple>
      <variable identifier="EMPTY"/>
   </multiple>
</setOutcomeValue>

Add Feedback to the Feedback Variable

This defines the feedback sections that will be shown to the user. Multiple feedback sections can be shown by adding multiple identifiers.

<setOutcomeValue identifier="FEEDBACK">
   <multiple>
      <baseValue baseType="identifier">INCORRECT</baseValue>
      <baseValue baseType="identifier">NOTSIMP</baseValue>
   </multiple>
</setOutcomeValue>

Append Feedback to the Feedback Variable

If some feedback has already been defined and you want to add further feedback, you can append another feedback identifier as shown.

<setOutcomeValue identifier="FEEDBACK">
   <multiple>
      <variable identifier="FEEDBACK"/>
      <baseValue baseType="identifier">SEEN-HINT</baseValue>
   </multiple>
</setOutcomeValue>

Comparisons

Equal

This simply compares whether two numerical values are equal. If ‘toleranceMode’ is set to exact, the values must be exactly equal. toleranceMode can also be set to absolute or relative, in which case the ‘tolerance’ must be specified. See the specification for more information.

<equal toleranceMode="exact">
   <variable identifier="iAnswer"/>
   <variable identifier="RESPONSE"/>
</equal>

EqualRounded

This compares whether two values are equal, to a specified level of rounding. The roundingMode can either be decimalPlaces or significantFigures.

<equalRounded roundingMode="decimalPlaces" figures="0">
   <variable identifier="RESPONSE"/>
   <variable identifier="iAnswer"/>
</equalRounded>

Substring contains

<substring caseSensitive="false">
   <baseValue baseType="string">+</baseValue>
   <variable identifier="printMath"/>
</substring>

isNull

This tests whether a value is null.

<isNull>
   <variable identifier="RESPONSE"/>
</isNull>

Custom Operator

Custom operators can be used to carry out more complicated tests, e.g. comparing mathematical responses using the MathAssess engine. The ‘action’ can take one of three values:

  • equal: algebraic equivalence (e.g. factorised and expanded forms are equivalent)
  • syntequal: syntactical equality (e.g. factorised and expanded forms are not syntactically equal)
  • code: allows custom comparison code to be provided

As an example of the difference between equal and syntequal, 1/2 and 2/4 are equal, but not syntequal. 2(x+1) and 2x+2 are equal, but not syntequal.

<customOperator class="org.qtitools.mathassess.CasCompare" ma:action="equal" ma:syntax="text/x-maxima">
   <variable identifier="mAnswer"/>
   <variable identifier="RESPONSE"/>
</customOperator>

The following example of a ‘code’ action tests whether the sum of the two response and answer is 0, i.e. the response is correct, but has the wrong sign.

<customOperator class="org.qtitools.mathassess.CasCompare" ma:action="code" ma:code="is(equal($1+$2,0));" ma:syntax="text/x-maxima">
   <variable identifier="RESPONSE"/>
   <variable identifier="mAnswer"/>
</customOperator>

Operators

And Or

As you would expected, placing two comparisons within an ‘and’ operator gives a true result only if both comparisons evaluate to true. Placing two comparisons within an ‘or’ gives a true result if either (or both) of the comparisons evaluate to true. For or, just use the following snippet and change the <and>/</and> tags to <or>/</or>

<and>
   <equal toleranceMode="exact">
      <variable identifier="iAnswer1"/>
      <variable identifier="RESPONSE1"/>
   </equal>
   <equal toleranceMode="exact">
      <variable identifier="iAnswer2"/>
      <variable identifier="RESPONSE2"/>
   </equal>
</and>

Not

The ‘not’ operators gives the ‘logical negative’ (i.e. opposite, e.g. true => false) of the subexpression. The following snippet will return true if the value of RESPONSE is not null.

<not>
   <isNull>
      <variable identifier="RESPONSE"/>
   </isNull>
</not>

 

Maths with QTI – Template Processing

Please note that this is a work in progress! I have added blocks that I just happen to have been using since I started writing this post, so they are not necessarily being added ‘simplest’ first. I will continue to add more as and when I use them.

After declaring all of the variables in an assessment item in a QTI XML file, the next step is to do the template processing. This is where variables are set or randomised, answers are calculated etc. There are too many possibilities for this section to get anywhere near specifying everything that is possible, and I won’t cover what can be done using Maxima in this post, but hopefully some of the blocks of code here can be used as they are, or will act as good starting points for doing other things.

templateProcessing Start and End

<templateProcessing>
   <!-- Template processing goes here -->
</templateProcessing>

Set Fixed values

Setting a Fixed Integer value

<setTemplateValue identifier="iA">
   <baseValue baseType="integer">2</baseValue>
</setTemplateValue>

Setting a Fixed Float value

<setTemplateValue identifier="fFloat">
   <baseValue baseType="float">3.14159</baseValue>
</setTemplateValue>

Set Randomised values

Set a positive Randomised Integer value from a range

<setTemplateValue identifier="iA">
   <randomInteger min="2" max="9"/>
</setTemplateValue>

Set a positive or negative Randomised Integer value from a range

<setTemplateValue identifier="iC">
   <product>
      <randomInteger min="1" max="10"/>
      <randomInteger min="-1" max="1" step="2"/>
   </product>
</setTemplateValue>

Note that the step attribute means that the second randomInteger will be selected only every other number in the specified range. In this case, that means it can only be -1 or 1, and not 0, so this defines whether the value is positive or negative.

Set an integer to a random value from within a range

<setTemplateValue identifier="iB">
   <random>
      <multiple>
         <baseValue baseType="integer">2</baseValue>
         <baseValue baseType="integer">3</baseValue>
         <baseValue baseType="integer">5</baseValue>
         <baseValue baseType="integer">8</baseValue>
         <baseValue baseType="integer">13</baseValue>
         <baseValue baseType="integer">21</baseValue>
      </multiple>
   </random>
</setTemplateValue>

Rounding

Round to a number of significant figures

<setTemplateValue identifier="fFloatRounded">
   <roundTo roundingMode="significantFigures" figures="3">
      <variable identifier="fFloat"/>
   </roundTo>
</setTemplateValue>

Round to a number of decimal places

<setTemplateValue identifier="fFloatRounded">
   <roundTo roundingMode="decimalPlaces" figures="2">
      <variable identifier="fFloat"/>
   </roundTo>
</setTemplateValue>

 

Constraints

Set a value to not equal a fixed value

<templateConstraint>
   <not>
      <equal toleranceMode="exact">
         <variable identifier="iA"/>
         <baseValue baseType="integer">7</baseValue>
      </equal>
   </not>
</templateConstraint>

Ensure Greatest Common Denomiator of 2 values is 1 (i.e. the values share no common factors other than 1)

<templateConstraint>
   <equal toleranceMode="exact">
      <gcd>
         <variable identifier="iA"/>
         <variable identifier="iB"/>
      </gcd>
      <baseValue baseType="integer">1</baseValue>
   </equal>
</templateConstraint>

Ensure 2 values are not equal

<templateConstraint>
   <not>
      <equal toleranceMode="exact">
         <variable identifier="iA"/>
         <variable identifier="iB"/>
      </equal>
   </not>
</templateConstraint>

Ensure one value is larger than another

<templateConstraint>
   <gt>
      <variable identifier="iB"/>
      <variable identifier="iC"/>
   </gt>
</templateConstraint>

Constraint to ensure a – (b + c) != 0, i.e. a != (b + c)

<templateConstraint>
   <not>
      <equal toleranceMode="exact">
         <subtract>
            <variable identifier="iA"/>
            <sum>
               <variable identifier="iB"/>
               <variable identifier="iC"/>
            </sum>
         </subtract>
         <baseValue baseType="integer">0</baseValue>
      </equal>
   </not>
</templateConstraint>

Conditions

Set iB to equal 6, 8 or 10 if iA < 5, otherwise iB equal to 2, 4 or 6

<templateCondition>
   <templateIf>
      <lt>
         <variable identifier="iA"/>
         <baseValue baseType="integer">5</baseValue>
      </lt>
      <setTemplateValue identifier="iB">
         <randomInteger min="6" max="10" step="2"/>
      </setTemplateValue>
   </templateIf>
   <templateElse>
      <setTemplateValue identifier="iB">
         <randomInteger min="2" max="6" step="2"/>
      </setTemplateValue>
   </templateElse>
</templateCondition>

Constants

<mathConstant name="pi"/>

Maxima Script Dummy Variable

I won’t cover what is possible with Maxima and hence what goes in this variable, but this is the code used for setting the dummy variable:

<setTemplateValue identifier="tDummy">
   <customOperator class="org.qtitools.mathassess.ScriptRule" ma:syntax="text/x-maxima">
      <baseValue baseType="string"><![CDATA[
         mAns : ev(iA*x^2iB,simp);
      ]]></baseValue>
   </customOperator>
</setTemplateValue>

Maths with QTI – Variable Declarations

Please note that this is a work in progress!

The first thing that you need to do in an assessment item, in a QTI XML file, is to declare your variables. These declarations come in 3 types – responseDeclarationoutcomeDeclaration and templateDeclaration.

responseDeclarations

This is where you declare variables used for responses and response processing. The following should be useful building blocks:

User Response

These are variables used to store the user’s response to a question.

<responseDeclaration identifier="RESPONSE_INTEGER" cardinality="single" baseType="integer"/><!-- integer response -->
<responseDeclaration identifier="RESPONSE_FLOAT" cardinality="single" baseType="float"/><!-- float response -->
<responseDeclaration identifier="RESPONSE_STRING" cardinality="single" baseType="string"/><!-- string response -->
<responseDeclaration identifier="RESPONSE_MATHS" cardinality="record"/><!-- maths response -->

printMath

This is a variable into which Presentation MathML form of a RESPONSE expression is copied.

<responseDeclaration identifier="printMath" cardinality="single" baseType="string"/>

Hint and Solution Request

These can be set to true when the hint or solution is requested, and should be tested at the start of the responseProcessing.

<responseDeclaration identifier="HINTREQUEST" cardinality="single" baseType="boolean"/>
<responseDeclaration identifier="SOLREQUEST" cardinality="single" baseType="boolean"/>

outcomeDeclarations

These are used for identifying and recording the outcomes from a user’s interactions, e.g. score and feedback.

Correct Response

These can be used to store the correct response to a question, for comparison with the user’s response.

<outcomeDeclaration identifier="iAnswer" cardinality="single" baseType="integer"/>
<outcomeDeclaration identifier="mAnswer" cardinality="record"/>

Score and Feedback

<outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float" normalMaximum="2">
   <defaultValue>
      <value>0</value>
   </defaultValue>
</outcomeDeclaration>
<outcomeDeclaration identifier="FEEDBACK" cardinality="multiple" baseType="identifier"/><!-- multiple cardinality - container for multiple values. For feedback, this means we can show multiple feedback sections at once -->
<outcomeDeclaration baseType="identifier" cardinality="single" identifier="EMPTY"/><!-- Empty value, for emptying FEEDBACK container -->

Hint and Solution Related

<outcomeDeclaration baseType="boolean" cardinality="single" identifier="seenSolution">
   <defaultValue>
      <value>false</value>
   </defaultValue>
</outcomeDeclaration>
<outcomeDeclaration baseType="boolean" cardinality="single" identifier="seenHint">
   <defaultValue>
      <value>false</value>
   </defaultValue>
</outcomeDeclaration>
<outcomeDeclaration baseType="identifier" cardinality="single" identifier="ASKHINT">
   <defaultValue>
      <value>askhint</value>
   </defaultValue>
</outcomeDeclaration>
<outcomeDeclaration baseType="identifier" cardinality="single" identifier="ASKSOLUTION">
   <defaultValue>
      <value>asksolution</value>
   </defaultValue>
</outcomeDeclaration>

templateDeclarations

These are used for defining and randomising variables used in the question itself.

Template Variables

<templateDeclaration identifier="iInt" cardinality="single" baseType="integer" mathVariable="true"/><!-- Integer -->
<templateDeclaration identifier="fFloat" cardinality="single" baseType="float" mathVariable="true"/><!-- Float -->
<templateDeclaration identifier="sString" cardinality="single" baseType="string" mathVariable="true"/><!-- String -->
<templateDeclaration identifier="mX" cardinality="record" mathVariable="true"/><!-- Maths -->

Use mathVariable=”true” to enable the variable to be used in MathML expressions.

Maxima Script Dummy Variable

<templateDeclaration identifier="tDummy" cardinality="single" baseType="boolean"/>

Maths with QTI – QTI XML Structure and Building Blocks Resource

We have recently started using QTI, specifically Uniqurate and QTIWorks, to write maths questions for delivery to students. Uniqurate can be used to write basic questions, including maths questions with numerical answers and simple randomisation of values. However, to incorporate more complex randomisation, e.g. randomising variable letters, to allow mathematical expressions to be given as answers and to do various other more complex things with maths questions, it is necessary to write (or, preferably, modify someone else’s!) QTI XML. This can get quite complicated and I have found some aspects of it difficult to get stuck into. I find that I know that something is possible, but it can take some time to pin down exactly how to do it, and I usually copy code from other questions. (Many thanks to Dr Sue Milne from ELandWeb for providing extensive help, both by giving access to her questions and answering many QTI-related queries)

One thing that I keep wishing for is a resource containing reusable blocks of QTI code that could be used to build a question. I believe that such a resource does not currently exist, so I have decided to start to write one, the links to which can be found below.

At this point I should make the disclaimer that I am still early on in my QTI journey, so I am no expert, and can by no means guarantee that this resource will be correct, but I will try to improve and extend it as I get to know the QTI specification better. The posts based on the XML snippets that I have been using, and therefore they are far from being a complete resourse. However, I hope they are useful to anyone getting started with QTI. Please feel free to suggest additions and corrections.

  1. QTI XML Basic Structure
  2. Variable Declarations
  3. Template Processing
  4. Item Body
  5. Response Processing
  6. Solution and Hint
  7. Modifying assessment.xml (for Assessments created in Uniqurate)

Maths with QTI – QTI XML Basic Structure

Please note that this is a work in progress!

This is the first of my QTI XML Building Blocks posts, where I will give the basic structure of an assessment item in a QTI XML file, that can be used as a framework that can be filled in with the building blocks that I will cover in future posts. This is very much a work in progress and I can’t promise everything is correct. At this stage I am just trying to create something that is useful to me, and putting it out there in case it is useful to anyone else!

So, here is the basic structure of a QTI XML document (or the basic structure I’ve been using/copying for writing questions):

<?xml version="1.0" encoding="UTF-8"?>

<!-- Define question properties -->
<!-- Some of these are obvious, others are XML namespace definitions - not all of these are generally needed 
   adaptive - if set to false, only one attempt is allowed, so this is best for summative items. If set to true, multiple attempts are allowed and the feedback/outcomes can be changed, and this is better for formative items, where a user can have multiple goes. This should not be confused with an adaptive test, where the questions presented change depending on a user's answers to previous questions, although a similar thing can be achieved within an adaptive item.
-->
<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_v2p1"
   xmlns:m="http://www.w3.org/1998/Math/MathML"
   xmlns:xi="http://www.w3.org/2001/XInclude"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:ma="http://mathassess.qtitools.org/xsd/mathassess"
   xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p1 imsqti_v2p1.xsd http://mathassess.qtitools.org/xsd/mathassess mathassess.xsd">
   xml:lang="en"
   adaptive="true"
   timeDependent="false"
   identifier="questionidentifier"
   title="Question Title">

   <!-- VARIABLE DECLARATIONS -->
   <!-- Here we define all of the variables that are going to be used in the question -->

   <!-- Response Declarations -->
   <!-- These are the variables used in response processing, including those for storing the response(s) given by the user -->
   <responseDeclaration identifier="RESPONSE" cardinality="single" baseType="integer"/>

   <!-- Outcome Declarations -->
   <!-- These are the variables used for returning an outcome to the user, e.g. score, feedback -->
   <outcomeDeclaration identifier="SCORE" cardinality="single" baseType="integer"/>

   <!-- Template Declarations -->
   <!-- These are the variables used for creating the question, e.g. the variables used for randomising values within a question -->
   <templateDeclaration identifier="iA" cardinality="single" baseType="integer" mathVariable="true"/>

   <!-- TEMPLATE PROCESSING -->
   <templateProcessing>
      <!-- Here template variables are assigned values, constraints are defined etc -->
   </templateProcessing>

   <!-- ITEM BODY -->
   <itemBody>
      <!-- The itemBody contains the content of the item, including the question, interactions, feedback sections (which are initially hidden) etc  -->
   </itemBody>

   <!-- RESPONSE PROCESSING -->
   <responseProcessing>
      <!-- Here the user's response is assessed, compared the correct answer etc, and outcome variable, e.g. score and which feedback to show, are set -->
   </responseProcessing>
</assessmentItem>