PREP 2013 Question Authoring - Archived

debugging

debugging

by Michael Doob -
Number of replies: 8
I have a debugging question. I have looked on the wiki pages and there seems to be very little about this. I guess I was hoping for some kind of debug mode. Specifically for one of my problems I get messages:

ERROR caught by Translator while processing problem file:tmpEdit/local/setdoob_workshop1_pm/solverank.pg.Doob.tmp
****************
Illegal division by zero at line 206 of (eval 5545)
   Died within main::reduce called at line 125 of (eval 5349)

The last line of the error message is easy to understand but not too helpful. It is simply a subroutine call:

125 reduce();

The warning messages at the end of the file says:

Use of uninitialized value $num in division (/) at line 206 of (eval 5545)
Processing of this PG problem was not completed. Probably because of a syntax error.
The translator died prematurely and no PG warning messages were transmitted. at /opt/webwork/webwork2/lib/WeBWorK/ContentGenerator/Problem.pm line 786.

Of course I have no idea of where $num comes from and can't do much to proceed. 

The perl code being used runs ok on my machine, and the major pieces work ok when done by hand as a debugging technique within the WeBWork problem.
Is there some secret flag I can invoke to see the exact source of the problem?

Cheers,
Michael

In reply to Michael Doob

Re: debugging

by Davide Cervone -
Here's what is happening: there is already a reduce() function in PGauxiliaryFunctions.pl, which is being loaded by PGstandard.pl, so your sub reduce doesn't seem to be redefining that, and the reduce() call you are making is actually to the original one. That macro is supposed to reduce two values to lowest terms, and it expects two arguments. Since you haven't passed it any, the one that it called $num is undefined, and so on line 206 of PGauxiliaryFunctions.pl, this is causing problems. I suspect there will be messages in the server log about attempts to redefine reduce(), but I haven't checked that.

There are two approaching to solving the problem: either rename your reduce() to something else (like Reduce() or ReduceMatrix()), or don't load PGstandard.pl and load the files it refers to manually.

I agree that the references to ... line x of (eval y) are not very helpful. At one point I had added code to translate the eval y references back to the files that they came from, but somewhere along the line that seems to have been removed. I will have to look into putting it back.

Davide
In reply to Davide Cervone

Re: debugging

by Michael Doob -
Thanks for your reply, which, as usual, goes right to the problem. I'm a little surprised that my definition didn't just replace the one in PGauxiliaryFunctions.pl, but I suppose that this would cause other odd problems.

Of course the subroutine really should take a matrix for a parameter. I assume that subroutines in perl are called by reference. Is the right to set up

sub Row_Reduce {
   my M@ = @_;

......

}

and then call

Row_Reduce(@C);

to row reduce $C in place? I'm attempting to pass the pointer.

Thanks for the help. It smooths out things significantly.

Cheers,
Michael


In reply to Michael Doob

Re: debugging

by Davide Cervone -
I suspect that your version of reduce() is compiled first, and then the loadMacros() runs, running the contents of the macro files dynamically, so they are compiled second, overriding your version. But I haven't experimented to see if that is true.

As for passing the matrix, I don't think this is the way you want to do it. Here are some issues to think about. Suppose you have an array

  @C = (1,2,3);
and then pass that to a function
  sub Example {
    my @M = @_;
    ...
  }
  
  Example(@C);
this means that you are actually passing the array by value, not by reference. That means the contents of the array @C are passed as three parameters to the function, just as though you had entered
  Example(1,2,3);
and @M will get a copy of the original array. So if you change @M in the subroutine, that doesn't change @C. (Although you could return the array @M and use
  @C = Example(@C);
though that is not the most efficient approach.)

When you make your matrix as

  @C = ([1,2],[3,4]);
this means that @C is an array of references to arrays, so when you pass this to the subroutine
  Example(@C);
you are passing the array by value, but the values passed are the references to the row arrays, so those references are copied, they still refer to the same rows. That means that if you did
  $M[0][1] = 100;
that would change the 2 to a 100 in the @C matrix. But if you did
  ($M[0],$M[1]) = ($M[1],$M[0]);
in the subroutine, that would not exchange the rows in @C

An alternative approach would be to use an array reference rather than an array, as in

  $C = [[1,2],[3,4]];

   Example($C);
In this case, you would use $C->[0][1] rather than $C[0][1] to get access to the 2 in the matrix. If you use
  sub Example {
    my $M = shift;
    ...
  }
then you can use $M->[$i][$j] within the subroutine, and if you do
  ($M->[0],$M->[1]) = ($M->[1],$M->[0]);
this will change $C as well, because you have passed a reference to the array of references (rather than passing all the row references by value).

It is also possible to pass a reference to @C. In Perl, you would use

  Example(\@C);
but in PG, all backslashes are doubled, so that doesn't work. Instead, PG uses ~~ to get a single backslash (awkward, but that's how it works). So you could use
    sub Example {
      my $M = shift;
      ...
    }

    @C = ([1,2],[3,4]);
    
    Example(\@C);
to pass the reference to @C.

Hope that answers your question.

Davide

In reply to Davide Cervone

Re: debugging

by Michael Doob -
Thanks Davide. Once again very helpful. Trying to google  an answer to my question produced a cacophony of somewhat contradictory references. I suppose that this reflects the history of perl.

That call out to sage: that's sweet! It inspires rethinking.

Cheers,
Michael
In reply to Michael Doob

Re: debugging

by Davide Cervone -
Yes, the sage thing is pretty cool. But remember that it does take more network access, and is one more point of possible failure. I would not abandon your original approach too quickly.

Davide
In reply to Davide Cervone

Re: debugging

by Michael Doob -
One thing I wanted to do with this course was create at least one problem interesting enough to put put into the problem bank.  I think that the problem in the set doob_workshop1_pm/problem 14 is pretty close. I'd like to throw out a couple of thoughts for consideration:

(1) I've broken out the rank 1 case separately because I think that there is some kind of typecasting to vectors going on that I don't understand. The symptom: if B is a matrix with one row, then tr(B)*B throws an error citing incompatible sizes. Both factors are considered as row matrices (or vectors, I suppose). I haven't found a way around this.

(2) Here's a syntax thing that is driving me crazy.  My radio buttons are defined by 

$mc = RadioButtons(
#   $my_choices,
    ["0", "1", "2", "3", "4", "5", "6"],
   "$rank");

I want to replace the fixed list with a constructed one which is dependent 
on the size of the matrix under consideration (= $matrix_size).

Something like

$my_choices = '[ "0" ';
for my $i (1..$matrix_size) { $my_choices .= ', "' . $i . '"'}
$my_choices .= " ]";

generates the right string but does not get interpreted correctly by the RadioButtons subroutine. I've tried many variations on this theme with identical (bad) results.  I'm embarrassed by the triviality, but I guess I need to break up the string in a way that is just unclear to me sad.

(3) Any comments (from anybody) to improve the code would be really welcome.

Cheers,
Michael

In reply to Michael Doob

Re: debugging

by Gavin LaRose -
Hi Michael,

Regarding the radio buttons options, it's probably worth noting that the syntax [ 0, 1, 2 ] is just a reference to an array. So we can generate arbitrary options by resorting to Perl. Before doing that, a couple of notes about Perl and references might be useful.

   @a = ( 0, 1, 2 ); # an array
   $a = \@a;         # a reference to that array
   $b = [ @a ];      # a reference to a copy of the array
   $c = [ 0, 1, 2 ]; # a reference to an anonymous array

With this in mind, it may be obvious(?) how to resolve your problem. If $matrix_size is the size of your matrix, then we could do any of

  @a = ();
  for ( my $i=0; $i<=$matrix_size; $i++ ) {
      $a[$i] = $i;
  }
  $mc = RadioButtons( [@a], $rank );
  # or
  @a = ( 0..$matrix_size ); # the array (0,1,...,$matrix_size)
  $mc = RadioButtons( [@a], $rank );
  # or
  $mc = RadioButtons( [(0..$matrix_size)], $rank );

Note that these take advantage of the fact that Perl is an untyped language: we can take @a=(0,1); or @a=("0","1"); and the two will be equivalent. If the options for the buttons were explicitly strings (e.g., "one", "two", etc.) we wouldn't be able to use the list expansion operator .., which fills in all values between the starting and ending one.

And I'm guessing that the rank 1 dimension error is the bug that Davide fixed recently; see my post to the main WeBWorK forum about dimension 1 matrices.

Hopefully that's helpful!
Gavin

In reply to Gavin LaRose

Re: debugging

by Michael Doob -
Thank you, Gavin. What you say makes perfect sense. In essence, I was supplying a scalar instead of a pointer. I definitely feel less crazy.

Cheers,
Michael