Lon-Capa

Lon-Capa is web-based software for doing homework, and much more. It's amazing what it can do. It's also amazing how hard it is to figure out how to do anything. I finally figured out how to get started from these Lon-Capa mini-lessons by Daniel Sewell. I couldn't even get started, until I realized one had to click on "switch server" in order to create homework problems. There is a GUI problem creator in Lon-Capa, but I prefer to write things in the source code (XML in this case). That way, you can cut and paste to create new problems, rather than clicking. So below is my attempt to understand how to create problems from scratch. The material here tries to abstract the relevant info from the Lon-Capa author manual.

Simple problems

A simple problem has two elements: text to ask the questions, and inputs to garner the student's responses. The text is just regular html, although you can include TeX within the <m>...</m> tags. For some reason, the text goes between <startouttext/> and <endouttext/> tags.

The responses go in response tags. There are many types: the usual ones like numerical, string, and options, but the math response is what distinguishes Lon-Capa from alternative such software. (There are other types of responses, too.) The math responses lets the student input something that either R or Maxima can understand and evaluate.

String, numerical, and formula responses

These are the easiest. All (almost) you need to do is provide the answer and a text input field:
<numericalresponse answer="2">
    <responseparam type="tolerance" default="0.005" name="tol" description="Numerical Tolerance" />
    <textline size="10"></textline>
</numericalresponse>

<stringresponse answer="NaN" type="ci">
    <textline size="10"></textline>
</stringresponse>

<formularesponse answer="a*b/(a+b)">
    <textline size="10"></textline>
</formularesponse>
The tolerance is optional. The default can also be a percentage, e.g., "2%". For the string response, type is "cs" or "ci", depending on whether the answer should be case sensitive or not. (There is also "mc", multiple choice.) The formula response is nice because the student can put in any formula that evaluates to the answer.

Multiple choice: Option and radio responses

There are several variations on multiple choice questions. In Lon-Capa, a foil is a choice, and its value is the correct response.

One of N choice

Radio response questions give the students a number of choices, exactly one of which is correct. Correct ones have value="true", so because there can only be one correct answer for the student, Lon-Capa will randomly choose one of the "true" responses. So below, one of the first two (true) will be shown to the student, as well as all of the others (false).
<radiobuttonresponse max="10" randomize="yes" direction="vertical">
    <foilgroup>
        <foil location="random" value="true" name="foil1">
            <startouttext />This is foil One.<endouttext />
        </foil>
        <foil location="random" value="true" name="foil2">
            <startouttext />This is foil Two.<endouttext />
        </foil>
        <foil location="random" value="false" name="foil3">
            <startouttext />This is foil Three.<endouttext />
        </foil>
        <foil location="random" value="false" name="foil4">
            <startouttext />This is foil Four.<endouttext />
        </foil>
        <foil location="random" value="false" name="foil5">
            <startouttext />This is foil Five.<endouttext />
        </foil>
    </foilgroup>
</radiobuttonresponse>
Other attributes: For a regular Yes/No question, use
<radiobuttonresponse max="10" randomize="no" direction="horizontal">
    <foilgroup>
        <foil value="true" name="foil1">
            <startouttext />Yes<endouttext />
        </foil>
        <foil value="false" name="foil2">
            <startouttext />No<endouttext />
        </foil>
    </foilgroup>
</radiobuttonresponse>
What if you want a drop-down box? See next type of question.

Several questions, each with same set of options

This type of question is good for matching, though it doesn't have to be one-to-one. (There is also a matchingresponse type, but I'm not sure what it adds to the proceedings.) Below, there are four foils, with answers Green, Red or Blue. Note that two have answer Red (value="Red").
<optionresponse max="10" randomize="yes">
    <foilgroup options="('Green','Red','Blue')">
        <foil location="random" value="Green" name="Foil1">
            <startouttext />This is foil One. It is currently set to "Green."<endouttext />
        </foil>
        <foil location="random" value="Red" name="Foil2">
            <startouttext />This is foil Two. It is currently set to "Red."<endouttext />
        </foil>
        <foil location="random" value="Blue" name="Foil3">
            <startouttext />This is foil Three. It is currently set to "Blue."<endouttext />
        </foil>
        <foil location="random" value="Red" name="Foil4">
            <startouttext />This is foil Four. It is currently set to "Red."<endouttext />
        </foil>
    </foilgroup>
</optionresponse>
The choices are the options within the foilgroup tag. Rather than radio buttons, the options are shown within drop-down boxes, one for each foil.

Thus, if you want a regular multiple choice but with a drop-down box, you use just one foil (still within a foilgroup):

<optionresponse max="10">
    <foilgroup options="('Green','Red','Blue')">
        <foil value="Green" name="the_foil">
            <startouttext />The only foil.<endouttext />
        </foil>
    </foilgroup>
</optionresponse>
[Compare this with the multiple choice using radio responses. There, the choices are the foils, and the values are true/false, though the student doesn't see these values explicitly. But here, the choices are the values, and the foil is the question.] To situate the dropdown box within the foil text, use <drawoptionlist /> at the desired location.

M of N choices

This type of question gives the students several statements (foils), and the goal is to choose which are true, i.e., the student must choose the correct M of N choices. The difference from the previous option response question is that here, since there are only two options for each foil (true or false), you can have check boxes so that "checked" means one of the options, and "unchecked" means the other. Use the checkboxvalue attribute to specify which option goes with checked, and checkboxoptions="nochoice" (not sure it's necessary).

The following sets up a question where the correct answer is to check foils 1, 3, and 5. They are the ones whose value is true.

<optionresponse max="10" randomize="no">
    <foilgroup options="('True','False')" checkboxvalue="True" checkboxoptions="nochoice">
        <foil name="Foil1" value="True">
            <startouttext />This is foil One. It is currently set to "True."<endouttext />
        </foil>
        <foil name="Foil2" value="False">
            <startouttext />This is foil Two. It is currently set to "False."<endouttext />
        </foil>
        <foil name="Foil3" value="True">
            <startouttext />This is foil Three. It is currently set to "True."<endouttext />
        </foil>
        <foil name="Foil4" value="False">
            <startouttext />This is foil Four. It is currently set to "False."<endouttext />
        </foil>
        <foil name="Foil5" value="True">
            <startouttext />This is foil Five. It is currently set to "True."<endouttext />
        </foil>
        <foil name="Foil6" value="False">
            <startouttext />This is foil Six. It is currently set to "False."<endouttext />
        </foil>
    </foilgroup>
</optionresponse>

Parts

A problem can either have no parts (so that the problem is just one big part), or have several parts. A part consists of a bunch of response types, as well as text. The function of a part is to give the student a submit button for the responses within that part. With no partitioning, there is one submit button for the entire problem, and the student either gets it right, meaning all responses are correct, or wrong, meaning there is at least one mistake, but no one knows where it is.

All one needs to do is wrap the desired responses in a <part> </part> environment. One warning: If there is at least one part, then every response has to be within some part's environment.

Randomizing & Scripting

The text and questions can be randomized, so that different students have different questions. The simplest approach is at the assignment construction level, where one can randomly choose questions from a set bank of questions for each. Within multiple-choice-type questions, we've already seen the possibilities of choosing random foils, and/or putting them in random order. There are also concept groups of foils, where foils can be arranged in groups, and one foil will be randomly chosen from each group. Thus one can be sure each student gets an appetizer, main course, and dessert.

But the real power comes in randomizing the actual questions students see, e.g., one can randomly generate numbers or formulas within questions. Almost anything can be done through scripting. The basic scripting language for Lon-Capa is perl, but with many added functions, including those that can interface with R and Maxima. Scripting can be used within the script environment in the problem, as well as within the answer environment within problems.

Setting up a problem

Suppose the question is to find the mean of a beta random variable, but you want different students to see different parameters. The variables within perl start with "$", so we might take the parameters to be $a and $b. We first have to set the script environment, where type="loncapa/perl" specifies perl with the added functions. The most useful function is &random($l,$u,$d), which returns a random number between $l and $u, in steps of $d. (There are also a number of functions for simulating from specific distributions.) So if we want our beta parameters to be integers between 2 and 10, we'd use
<script type="loncapa/perl">
   $a = &random(2,10,1);
   $b = &random(2,10,1);
   $mu = $a/($a+$b);
</script>
Note we've also put the mean of the beta in the variable $mu. Now within the text, we can use $a and $b directly. So the question could be
<startouttext />
   Find the mean of a Beta($a,$b) random variable.
<endouttext />
Or, if we want it to look more math-y, we can use TeX, but add the eval="on" attribute in the math tag:
<startouttext />
   Find the mean of a <m eval="on">$Beta($a,$b)$</m> random variable.
<endouttext />
Also, and quite importantly, we can put the $mu in the answer. This problem uses a numerical response:
<numericalresponse answer="$mu">
    <responseparam type="tolerance" default="0.01" name="tol" description="Numerical Tolerance" />
    <textline size="10"></textline>
</numericalresponse>

More complex evaluations

The questions so far have had answers that are easy to evaluate. Sometimes you need an algorithm to figure out the answer. For example, in probability problems you might be happy with fraction answers, such as 1/3 instead of 0.3333.... The numerical response problem doesn't accept 1/3. You could use a formula response problem, setting a tolerance as well perhaps. Or use a math response type, with the answer evaluated by R (or Maxima). For this type, the answer is specified in an answer block within the response block, rather than as an attribute. But you can set the answerdisplay attribute, which is what is presented to the student as the correct answer. The code is
<mathresponse answerdisplay="1/3" cas="R">
   <answer>
      abs(RESPONSE[1]-1/3)< 0.001 
   </answer>
   <textline readonly="no" size=" 8 " />
</mathresponse>
Within the answer block you can put any sequence of R statements, as long as the final one returns true or false. The student's input is placed in the RESPONSE array, the components consisting of the comma-separated input. (I mean "a,12,log(18)" resolves to RESPONSE[1]="a", RESPONSE[2] = "12", RESPONSE[3] = "log(18)".) To get scripting variables (like $x) into the answer block, add in the args attribute to the math response tag. This attribute can then be set to another comma-separated list. Elements are specified using LONCAPALIST[i]. E.g., using the beta example from above, one could code the response block as
<mathresponse answerdisplay="$mu" cas="R" args="$mu">
   <answer>
      abs(RESPONSE[1]-LONCAPALIST[1])< 0.001 
   </answer>
   <textline readonly="no" size=" 8 " />
</mathresponse>
Maxima can be used in the answer block by specifiying cas="maxima".

The custom response type uses perl to evaluate the input. Since it is perl, the scripting variables can be used as is. The student's input is in the variable $submission. (If there are several input fields in the question, then the inputs will be in $$submission[1], $$submission[2], etc.) The perl code has to return a special Lon-Capa word indicating the result of the evaluation. There are about ten of them, but the two simplest are EXACT_ANS, meaning the student is correct, and INCORRECT. The beta response block would then be written

<customresponse answerdisplay="$mu">
   <answer type="loncapa/perl">
      if(abs($submission-$mu)< 0.001) {return('EXACT_ANS');}
      return("INCORRECT");
   </answer>
   <textline readonly="no" size=" 8 " />
</customresponse>

Using R in the script

The Lon-Capa perl functions used to interface with R or Maxima are &cas($w,$s,$l) and &cas_hashref($w,$s,$l). The first argument is a string indicating which language to use, "R" or "maxima". The second argument is a string containing the commands to use. The third lets you specify libraries to load, but I'm not sure how it works in R.

The commands will return either a singleton, a vector, a matrix, or a list. (I think that's about it.) If the result is one of the first three types, then the first function &cas can be used. The output will be a string of the values, e.g., "1 2 3 4". The matrix structure is lost. This is a good choice if the answer is a singleton. For example, if you want the correlation between two independent random uniform vectors of size three, the following statement (within a script block) would place the correlation in $cor.

$cor = &cas("R","x<-runif(3);y<-runif(3);cor(x,y)");
The function &cas_hashref can always be used, and for lists it is the only one that works. Its output is a vector of two strings. The first is the address (I guess) of the result of the R calculations, or the value if the result is a singleton. The second is a dump of the result. For example, consider doing a regression with the above vectors:
 ($data,$dump) = &cas_hashref("R","x<-runif(3);y<-runif(3);lm(y~x);"); 
Then $data is HASH(0x55555ec16330), and $dump is

$VAR1 = { 'coefficients' => \{ '(Intercept)' => '-0.5160445', 'x' => '1.868763' }, 'residuals' => \{ '1' => '-0.7194856', '2' => '0.3506257', '3' => '0.3688598' }, 'effects' => \{ '(Intercept)' => '-0.7298277', 'x' => '-1.269063', 'result2' => '0.8812806' }, 'rank' => '2', 'fitted.values' => \{ '1' => '0.4062064', '2' => '1.326213', '3' => '-0.4683212' }, 'assign' => \{ '1' => '0', '2' => '1' }, 'qr' => \{ 'qr' => \{ '1' => \{ '1' => '-1.732051', '2' => '-0.8688332' }, '2' => \{ '1' => '0.5773503', '2' => '-0.6790929' }, '3' => \{ '1' => '0.5773503', '2' => '-0.6966859' } }, 'qraux' => \{ '1' => '1.577350', '2' => '1.717376' }, 'pivot' => \{ '1' => '1', '2' => '2' }, 'tol' => '1e-07', 'rank' => '2' }, 'df.residual' => '1', 'xlevels' => \{}, 'call' => \{ '1' => 'lm', '2' => 'y ~ x' }, 'terms' => \{ '1' => '~', '2' => 'y', '3' => 'x' }, 'model' => \{ 'y' => \{ '1' => '-0.3132791', '2' => '1.676839', '3' => '-0.09946135' }, 'x' => \{ '1' => '0.4935089', '2' => '0.985817', '3' => '0.02553737' } } };

That's not very useful as is. There are a couple of functions useful for extracting results from the $data element. (You can ignore the $dump info.) To obtain a single value, use the entry form of the function. E.g., the slope of the regression is in the "x" element of the "coefficients" list. Thus

 $slope = &cas_hashref_entry($data,"coefficients","x"); 
yields the slope. An element of an array is obtained similarly. For example, to find an element of the outer product of the two vectors:
($data2,$dump2) = &cas_hashref("R","x<-runif(3);y<-runif(3);outer(x,y);");
$z = &cas_hashref_entry($data2,"2","3");
gets the (2,3) element of the matrix. To extract a vector, into a perl array, use the array form. For example, to obtain the residuals from the above regression, use
 @resid = &cas_hashref_array($data,"residuals"); 
For more information, including for Maxima, see http://www.lon-capa.org/cas.html

Variables in LaTeX

To format expressions with LaTeX (or TeX), use the "<m>" tag. Then for inline expressions:
<m>$E[X] = \frac{\Gamma(\alpha+\beta)}{\Gamma(\alpha)\Gamma(\beta)}.$</m>
Use $$ for display. If you want scripting variables, like $a, to be evaluated within the TeX, then add the eval="on" attribute to the tag:
<m on="eval">$E[X] = \frac{\Gamma($a+$b)}{\Gamma($a)\Gamma($b)}.$</m>