WeBWorK Problems

Variable within a Matrix

Variable within a Matrix

by Marie-Claude Bonneau -
Number of replies: 2
Hello,
Is it possible to have WeBWorK recognize a variable within a matrix? Here is my code and the B matrix is not being recognized as being "correct" yet WeBWorK knows what it is.

########
DOCUMENT();

loadMacros(
"PGstandard.pl",
"MathObjects.pl",
"MatrixUnits.pl",
"MatrixReduce.pl",
"PGmatrixmacros.pl",
"contextFraction.pl",
"PGunion.pl",
"parserPopUp.pl",
"scaffold.pl"
);

TEXT(beginproblem());
$showPartialCorrectAnswers = 1;

Context("Matrix");
Context()->variables->add(k => 'Real');
$kVar = Compute("k");

$a11 = 1;
$a12 = random(-4,-1,1);
$a13 = random(- $a12 +2, 10,1);
$d = random(-4,4,1);
$e = random(-4,4,1);
$g = 2*random(-3,3,1)+1;
$h = non_zero_random(-3,3,1);
$i = non_zero_random(-20,20,1);

$a21 = non_zero_random(-5,5,2);
$a22 = $a21*$a12+ $g;
$a23 = $a21*$a13+$g*$d;

$a31 = $a21+$h;
$a32 = $a21*$a12+$g+$h*$a12;
$a33 = ($a21+$h)*$a13 + $g*$d;

$a33var = $kVar;

$b1 = random(-4,4,1);;
$b2 = $a21*$c+ $g*$e;
$b3 = $kVar;

$b3number = random(2,5,1);

Context("Matrix");
Context()->{cmpDefaults}{Matrix} = {close=>'|',formMatrix => 1};
Context()->lists->set(Matrix=>{close=>'|',formMatrix => 1});
Context()->parens->set("[" => {type => "Matrix", close=>'|'});
Context()->variables->add(k => 'Real');
$A1 = Matrix([[$a11,$a12,$a13],[$a21,$a22,$a23],[$a31,$a32,$a33]]);
$A2 = Matrix([[$a11,$a12,$a13],[$a21,$a22,$a23],[$a31,$a32,$a33]]);
#$A3 = Matrix([[$a11,$a12,$a13],[$a21,$a22,$a23],[$a31,$a32,$a33var]]);

Context("Matrix");
Context()->{cmpDefaults}{Matrix} = {open=>'',formMatrix => 1};
Context()->lists->set(Matrix=>{open=>'',formMatrix => 1});
Context()->parens->set("" => {type => "Matrix", open=>"", close => "]"});
$B1 = Matrix([[$b1], [$b2], [$b3number]]);
$B2 = Matrix([[$b1], [$b2], [$b3]]);
$B3 = Matrix([[$b1], [$b2], [$b3number]]);

Context("Numeric");
Context()->variables->add(y => 'Real',z => 'Real');
Context()->noreduce('(-x)-y');
Context()->noreduce('(-x)+y');
Context()->variables->add(k => 'Real');
$Eq1 = Formula("$a11 x + $a12 y + $a13 z")->reduce;
$Eq2 = Formula("$a21 x + $a22 y + $a23 z")->reduce;
$Eq3 = Formula("$a31 x + $a32 y + $a33 z")->reduce;
$Eq33 = Formula("$a31 x + $a32 y + $a33var z")->reduce;

###########################################
Context()->texStrings;
BEGIN_TEXT
$BR
$BBOLD System with numbers turned into augmented matrix $EBOLD $BR
$BR
\{
BeginTable(border=>0, tex_border=>"1pt", spacing=>0, padding=>2, center=>0).
AlignedRow(["\( $Eq1 \)","\( = \)","\( $b1 \)"], align=>RIGHT, separation=>0, indent=>10).
AlignedRow(["\( $Eq2 \)","\( = \)","\( $b2 \)"], align=>RIGHT, separation=>0, indent=>10).
AlignedRow(["\( $Eq3 \)","\( = \)","\( $b3number \)"], align=>RIGHT, separation=>0, indent=>10).

EndTable()
\}
$BR $BR
We have $BR $BR
\( \quad \) \{ $A1->ans_array(2) \} \{ $B1->ans_array(2) \}
$BR $BR and this works!! WeBWorK recognizes both parts of the augmented matrix.
$BR
$BR
$HR $HR $BR
END_TEXT
Context()->normalStrings;

# with numbers
ANS( $A1->cmp() );
ANS( $B1->cmp() );

###########################################
Context()->texStrings;
BEGIN_TEXT
$BR
$BBOLD System with a variable in b's turned into augmented matrix $EBOLD $BR
$BR
\{
BeginTable(border=>0, tex_border=>"1pt", spacing=>0, padding=>2, center=>0).
AlignedRow(["\( $Eq1 \)","\( = \)","\( $b1 \)"], align=>RIGHT, separation=>0, indent=>10).
AlignedRow(["\( $Eq2 \)","\( = \)","\( $b2 \)"], align=>RIGHT, separation=>0, indent=>10).
AlignedRow(["\( $Eq3 \)","\( = \)","\( $b3 \)"], align=>RIGHT, separation=>0, indent=>10).

EndTable()
\}
$BR $BR
We have $BR $BR
\( \quad \) \{ $A2->ans_array(2) \} \{ $B2->ans_array(2) \}
$BR $BR and this does not work. WeBWorK only recognizes the matrix of coefficient yet WeBWorK knows that \( B2 = $B2 \)
$BR I also get a warning : The evaluated answer is not an answer hash : ||.
$BR
but I do not have enough experience to know what this warning is being produced :(
$BR
$BR
$HR $HR $BR
END_TEXT
Context()->normalStrings;

# with variable
ANS( $A2->cmp() );
ANS( $B2->cmp() );

###########################################
Context()->texStrings;
BEGIN_TEXT
$BR
$BBOLD System with a variable in matrix of coefficient turned into augmented matrix $EBOLD $BR
$BR
\{
BeginTable(border=>0, tex_border=>"1pt", spacing=>0, padding=>2, center=>0).
AlignedRow(["\( $Eq1 \)","\( = \)","\( $b1 \)"], align=>RIGHT, separation=>0, indent=>10).
AlignedRow(["\( $Eq2 \)","\( = \)","\( $b2 \)"], align=>RIGHT, separation=>0, indent=>10).
AlignedRow(["\( $Eq33 \)","\( = \)","\( $b3number \)"], align=>RIGHT, separation=>0, indent=>10).

EndTable()
\}
$BR $BR and this does not work. I was not able to properly define the matrix A3
$BR
$BR
END_TEXT
Context()->normalStrings;

ENDDOCUMENT(); # This should be the last executable line in the problem.
###########

Thank you.
Marie-Claude
In reply to Marie-Claude Bonneau

Re: Variable within a Matrix

by Davide Cervone -
Because you are continually changing the context, you are losing track of the variable k. In particular, the context in which you are creating the $B2 matrix does not have the variable k. Moreover, the context that is associated with the $kVar (and so $b3) variable is actually the original Numeric context, so your are mixing contexts in a way that will cause problems.

It appears that you are resetting and adjusting the contexts in order to set the opening and closing delimiters for the matrices, but you are working much harder than necessary for that. You could set the Matrix content once, and not modify its lists or other values (other than adding the variable k), and modify the delimiters on the matrices themselves, as in:

$A1 = Matrix([[$a11,$a12,$a13],[$a21,$a22,$a23],[$a31,$a32,$a33]])->with(close=>"|");
$B1 = Matrix([[$b1], [$b2], [$b3number]])->with(open=>".");
(we use an open delimiter of . so that in TeX it is `\left. ... \right]`, which produces no left delimiter).

Of course, this requires you to do this for each matrix produced, but that is probably easier than the complicated and opaque resetting of the Matrix context for each set.

One of the consequences of your approach is that the A and B matrices will each have their own line in the results table when a student enters an answer. You can improve that by using the MultiAnswer object to bind the two matrices together in one line of the results table. In fact, you can automate most of the process using something like the following:

loadMacros(
  "PGstandard.pl",
  "parserMultiAnswer.pl",
  "PGcourse.pl"
);

TEXT(beginproblem());

sub AugmentedMatrix {
  my ($A, $B) = @_;
  my ($a, $b, $c) = @$B;
  $A = Matrix($A)->with(close => '|');
  $B = Matrix([[$a], [$b], [$c]])->with(open => '.');
  MultiAnswer($A, $B)->with(
    singleResult => 1,
    separator => "",
    tex_separator => "",
    checker => sub {
      my ($c, $s, $self, $ans) = @_;
      my ($cA, $cB) = @$c;
      my ($sA, $sB) = @$s;
      $self->{ans}[0]{preview_latex_string} =~ s/~~]$/|/;
      $self->{ans}[1]{preview_latex_string} =~ s/~~[/./;
      return $cA == $sA && $cB == $sB;
    }
  );
}

$showPartialCorrectAnswers = 1;

Context("Matrix");
Context()->variables->add(k => 'Real', x => 'Real', y => 'Real');
Context()->noreduce('(-x)-y');
Context()->noreduce('(-x)+y');

$k = Compute("k");

$a11 = 1;
$a12 = random(-4,-1,1);
$a13 = random(- $a12 +2, 10,1);
$d = random(-4,4,1);
$e = random(-4,4,1);
$g = 2*random(-3,3,1)+1;
$h = non_zero_random(-3,3,1);
$i = non_zero_random(-20,20,1);

$a21 = non_zero_random(-5,5,2);
$a22 = $a21*$a12+ $g;
$a23 = $a21*$a13+$g*$d;

$a31 = $a21+$h;
$a32 = $a21*$a12+$g+$h*$a12;
#$a33 = ($a21+$h)*$a13 + $g*$d;
$a33 = $k;

$b1 = random(-4,4,1);
$b2 = $a21*$c+ $g*$e;
$b3 = $k;

$M1 = AugmentedMatrix(
  [[$a11, $a12, $a13],
   [$a21, $a22, $a23],
   [$a31, $a32, $a33]],
  [$b1, $b2, $b3]
);

$Eq1 = Formula("$a11 x + $a12 y + $a13 z")->reduce;
$Eq2 = Formula("$a21 x + $a22 y + $a23 z")->reduce;
$Eq3 = Formula("$a31 x + $a32 y + $a33 z")->reduce;

###########################################

Context()->texStrings;
BEGIN_TEXT

From the system of equations

\[\begin{aligned}
$Eq1 &= $b1\\
$Eq2 &= $b2\\
$Eq3 &= $b3
\end{aligned}\]
we have $BR $BR
\( \quad \) \{ $M1->ans_array(2) \}\{ $M1->ans_array(2) \}

END_TEXT
Context()->normalStrings;

ANS($M1->cmp());
Here, we define AugmentedMatrix() to encapsulate most of the work. It creates the MultiAnswer object by taking your A matrix and B vector and creating separate Matrix objects for them, with the proper delimiters. Its answer checker checks that both matrices match the correct ones, and also fixes the delimiters in the output for the student-entered matrices (since these get the usual delimiters by default). It also marks the MultiAnswer matrix so that it is a single result in the table, and formats the results so that the two matrices are adjacent (so they look like the augmented matrix).

Once the augmented matrix is created, you can generate its input arrays using the $M1 variable just as you did the A and B variables (though I recommend not putting a space between them for better formatting). The output in the results table will be formatted like an augmented matrix except for the situation where the student leaves the A part entirely blank. One could work harder to make even that case display properly, but that would take creating your own MathObject class, and is beyond the scope of this answer.

You also seem to be working too hard to format your system of equations, which doesn't require the use of BeginTable/EndTable. Indeed, using that will make your question less accessible to students using screen readers, for example. So my example uses the aligned environment to format the math.

Using PGML would make the problem even easier to write:

loadMacros(
  "PGstandard.pl",
  "parserMultiAnswer.pl",
  "PGML.pl",
  "PGcourse.pl",
);

sub AugmentedMatrix {
  my ($A, $B) = @_;
  my ($a, $b, $c) = @$B;
  $A = Matrix($A)->with(close => '|');
  $B = Matrix([[$a], [$b], [$c]])->with(open => '.');
  MultiAnswer($A, $B)->with(
    singleResult => 1,
    separator => "",
    tex_separator => "",
    checker => sub {
      my ($c, $s, $self, $ans) = @_;
      my ($cA, $cB) = @$c;
      my ($sA, $sB) = @$s;
      $self->{ans}[0]{preview_latex_string} =~ s/~~]$/|/;
      $self->{ans}[1]{preview_latex_string} =~ s/~~[/./;
      return $cA == $sA && $cB == $sB;
    }
  );
}

$showPartialCorrectAnswers = 1;

Context("Matrix");
Context()->variables->add(k => 'Real', x => 'Real', y => 'Real');
Context()->noreduce('(-x)-y');
Context()->noreduce('(-x)+y');

$k = Compute("k");

$a11 = 1;
$a12 = random(-4,-1,1);
$a13 = random(- $a12 +2, 10,1);
$d = random(-4,4,1);
$e = random(-4,4,1);
$g = 2*random(-3,3,1)+1;
$h = non_zero_random(-3,3,1);
$i = non_zero_random(-20,20,1);

$a21 = non_zero_random(-5,5,2);
$a22 = $a21*$a12+ $g;
$a23 = $a21*$a13+$g*$d;

$a31 = $a21+$h;
$a32 = $a21*$a12+$g+$h*$a12;
#$a33 = ($a21+$h)*$a13 + $g*$d;
$a33 = $k;

$b1 = random(-4,4,1);
$b2 = $a21*$c+ $g*$e;
$b3 = $k;

$M1 = AugmentedMatrix(
  [[$a11, $a12, $a13],
   [$a21, $a22, $a23],
   [$a31, $a32, $a33]],
  [$b1, $b2, $b3]
);

$Eq1 = Formula("$a11 x + $a12 y + $a13 z")->reduce;
$Eq2 = Formula("$a21 x + $a22 y + $a23 z")->reduce;
$Eq3 = Formula("$a31 x + $a32 y + $a33 z")->reduce;

###########################################

BEGIN_PGML
From the system of equations

>>  [``\begin{aligned}
      [$Eq1] &= [$b1]\\
      [$Eq2] &= [$b2]\\
      [$Eq3] &= [$b3]
      \end{aligned}``] <<

we have the augmented matrix

    [__]*{$M1}[__]*{$M1}

END_PGML
Finally, note that you are are including lots of macro files that you don't use. This just causes unnecessary load on the server as it has to load and compile those file for no reason every time one of your students views or submits an answer to your problem. So it is best to use only the macro files that you actually need.