s(n) = delta(n) + 3*delta(n1)  2*delta(n2)
1) can I define a Kronecker delta in Webwork? delta(n) = 1 for n = 0 and 0 else
2) if so, can the students use that function in answers?
3) I don't know that it is necessary, but can I restrict the arguments of s(.) to integers? I think I can get around this by specifying the test points to be integers
4) we use the unit step function a great deal. And make the substitution (or equivalence) of step(x) = u(x). Given the restriction to integers, could the kronecker delta be defined in terms of the unit step? creating a narrow pulse about zero?
5) is there a "nice" way to indicate the zero position in a sequence like the one s(n) = {..., 0,1,3,2, 0 ...}? In Latex we can underline or use uparrows.
6) what about the extension to the Dirac delta in the continuous domain? I thought I'd ask.

We can add functions to the context in a problem; this is discussed on this page in the
problem techniques section of the WeBWorK
documentation wiki. The subroutine
sub delta {
shift; my $x = shift;
return ($x == 0 ? 1 : 0);
}
might do what you want.  If we define the function as noted in (1), students will be able to use it.
 You can restrict formulas to be evaluated only at integers as discussed here. Note the comment about having a variable in the context that is only evaluated at integers, in the right column.
 The step function is discussed on this page in the problem techniques documents. I'm not sure if we can define the step or delta in terms of each other, or if we would want to.
 I'm not sure what you're asking for the sequence question. In problems you have access to LaTeX markup, so we could resort to that to indicate the zero element—though how this is done might depend on the definition of the sequence.

The above might deal with
delta(x)
, withx
as a real.
A couple of thoughts:

First, you have
Context()>variables>add(n=>'Real', k=>'Real');
, so there are actually three variables in your problem (the default, x, plus the two you added, n and k). So when you set the test points you need to specify all three (in alphabetical order). Thus I think yourtest_points
declaration,$X >{test_points} = [[0],[4],[$N1]];
, should specify three values per point:$X>{test_points} = [[0,0,0],[1,1,1],[2,2,2],[3,3,3],[$N1,$N1,$N1]];
. 
Similarly, where you had
[[0]..[$N1]]
I think it will fail because the..
operator generates lists of incrementable values (like integers and letters). Here[0],[1]
(etc.) are actually (references to) arrays of one element, for which there isn't an obvious increment.
This last brings up a couple of things about Perl that we didn't really discuss in the workshop:

@a = (0,'a',1,3)
is an array of four elements, with$a[1] = 'a'
, etc., but 
$a = ['a',1,4,9]
is a reference to an array. The square brackets in Perl create the array reference. The same thing can be obtained by setting@b = ('a',1,4,9)
and then$a = \@b
to set$a
as a reference to the array@b
. A similar thing can be done by taking$a = [@b]
, but this then makes$a
a reference to a copy of@b
(so that if we then change one, we don't change the other). 
Elements of the referenced array are obtained with
$a>[index]
(so that$a>[2]
from above is equal to4
). Note that this is the same operator we use to invoke a method of an object. 
Similarly,
%c = ( a=>1, b=>5 );
is a hash (dict in python and other languages), while 
$d = { a=>1, b=>5 }
is a reference to a hash. Elements of the reference are also obtained with the>
operator:$d>{b}
is equal to5
, in the same way$c{b}
from above would be equal to5
. (Note that this is the same syntax as things like$X>{test_points}
, which suggests, correctly, that what is really going on there is that we are accessing values in a hash reference.)
Gavin
$P = []; for ($m=0; $m[$m] = [$m,$m]; } $X >{test_points} = $P; and
$X>{tet_points} = []; for ($m=0; $m{test_points}[$m] = [$m,$m]; } How does PG know to stop at $N1?
I get the error
ERRORS from evaluating PG file: Undefined subroutine &main::u called at line 135 of (eval 11115)the line in the code is
135 for ($k = 2;$k<=2;$k++) {$ut[$k+2]=u($k);}I originally did a quick and dirty kroneker delta definition
#parserFunction("kdelta(n)" => "u(n+0.1)u(n0.1)"); #kronecker delta approximationwhich worked  i.e., I could evaluate kdelta OK, but when I tried to evaluate u(n) I got the error.
The entire code is below
##DESCRIPTION ## Algebra problem: true or false for inequality ##ENDDESCRIPTION ##KEYWORDS('algebra', 'inequality', 'fraction') ## DBsubject('Algebra') ## DBchapter('Fundamentals') ## DBsection('Real Numbers') ## Date('6/3/2002') ## Author('') ## Institution('') ## TitleText1('Precalculus') ## EditionText1('3') ## AuthorText1('Stewart, Redlin, Watson') ## Section1('1.1') ## Problem1('22') ######################################################################## DOCUMENT(); # modified to use j for i = sqrt(1) $complexJ =1; # 0 => i, 1 => j $I = ($complexJ)? 'j': 'i'; loadMacros( "PGstandard.pl", # Standard macros for PG language "MathObjects.pl", "AnswerFormatHelp.pl", ($complexJ) ? "contextComplexJ.pl" : "", #"source.pl", # allows code to be displayed on certain sites. "PGcourse.pl", # Customization file for the course ); # DTFT of aperiodic series geometric series TEXT(beginproblem()); # Show which answers are correct and which ones are incorrect $showPartialCorrectAnswers = 1; ############################################################## # # Setup # # Context("Complex"); Context()>variables>add(n=> 'Real'); Context()>variables>remove(z); # only variables should be n Context()>functions>add( u => { class => 'Parser::Legacy::Numeric', perl => 'Parser::Legacy::Numeric::do_step' }, ); # tried this and include ParserFunction.pl #parserFunction("kdelta(n)" => "u(n+0.1)u(n0.1)"); #kronecker delta approximation # this works if the student and answer checker evaluates functions at integers # use kdelta defined by LaRose package NewFunc; # this next line makes the function a # function from reals to reals our @ISA = qw(Parser::Function::numeric); sub kdelta { shift; my $x = shift; return ($x == 0 ? 1 :0); } # end subroutine package main; # Make it work on formulas as well as numbers sub kdelta {Parser::Function>call('kdelta',@_)} # Add the new functions to the Context Context()>functions>add( kdelta => {class => 'NewFunc', TeX => '\delta'}, ); $a = non_zero_random(2,2,1); # nonzero offset $n0 = random(6,3,1); # lower_limit $n0inc = random(3,5,1); # range of formula 1 $n1 = $n0 + $n0inc; # upper limit of formula 1 $n2 = random($n1+1,$n1+5,1); # lower_limit of formula 2 $n2inc = random(3,5,1); # range of formula 2 $n3 = $n2 + $n2inc; # upper limit of formula 2 $lower_lim = $n01; $upper_lim = $n3+1; $lower_lim_p1 = $lower_lim +1; # need for text index $D = random(2,8,1); # denominator of fraction in f1 $Nx = $upper_lim  $lower_lim +1; # number of samples for x(n) # compute limits for y(n) = x(n+n0) $n1y = $n3 +$n0; # should be negative $n3y = $n1 + $n0; #should be positive $maxlim = max($n1y,$n3y); # make limits symmetric $lower_limy = $maxlim; $upper_limy = $maxlim; $lower_limy_p1 = $lower_limy +1; # need for text index $Ny = $upper_limy  $lower_limy +1; # number of samples for y(n) $f1 = Formula("$a+ n/$D")>reduce; # formula 1 $f2 = Formula("1")>reduce; # formula 2 $f3 = Formula("($a+n/$D)*(u(n$n0)u(n$n11)) +u(n$n2)u(n$n31)")>reduce; #answer $f3>{test_points} = [[$n01], [$n0], [$n0+1], [$n11], [$n1], [$n1+1],[$n21], [$n2], [$n2+1],[$n31], [$n0], [$n3+1]]; # alternative answer $f4 = Formula("($a+n/$D)*(u(n$n0)u(n$n11)) +kdelta(n$n2)+kdelta(n$n2+1)+kdelta(n$n2+2)+kdelta(n$n2+3)+kdelta(n$n2+4)")>reduce; $f4>{test_points} = [[$n01], [$n0], [$n0+1], [$n11], [$n1], [$n1+1],[$n21], [$n2], [$n2+1],[$n31], [$n0], [$n3+1]]; #for ($k = $n0;$k<=($n3);$k++) {$xcheck1[$k] = $f3 > eval(n =>$k);} #for ($k = $n0;$k<=($n3);$k++) {$xcheck2[$k] = $f4 > eval(n =>$k);} # Compute x(n) for ($k = 0;$k<=$Nx1;$k++) {$x[$k]=0;} for ($k = 1;$k<=($n0inc+1);$k++) {$x[$k] = $f1 > eval(n =>($n0+$k1));} for ($k = 1+$n2$n0;$k<=($n2inc+$n2$n0+1);$k++) {$x[$k] = 1;} # compute y(n) $yindx0 = $upper_limy; $yindx_start = $yindx0  $upper_lim+$n0; for ($k = 0;$k<=$Ny1;$k++) {$y[$k]=0;} for ($k = 0;$k<=$Nx1;$k++) {$y[$yindx_start+$k] = $x[$Nx1 $k];} for ($k = 0;$k<=3;$k++) {$d[$k]=kdelta(2$k);} for ($k = 2;$k<=2;$k++) {$ut[$k+2]=u($k);} ############################################################## # # Text # # Context()>texStrings; BEGIN_TEXT This problem is related to 2.1 in the text. $PAR Given the discretetime signal, $n0, $n1, $n2, $n3, $d[1], $d[2], $d[3], $ut[1],$ut[1] \[ x(n) = \left\lbrace \begin{array}{ l l } $f1 & \mbox{ if } $n0 \leq n \leq $n1 \\ 1 & \mbox{ if } $n2 \leq n \leq $n3 \\ 0 & \mbox{ else. } \end{array} \right. \] $PAR (a) determine the values for \( $lower_lim \leq n \leq $upper_lim \) $BR Enter the values as a vector of the form: \( < x($lower_lim),x($lower_lim_p1), ... , x($upper_lim) \)> $BR \{ ans_rule(60)\} \{ AnswerFormatHelp("vectors") \} $BR (b) for \( y(n) = x(n+$n0) \), determine the values for \( $lower_limy \leq n \leq $upper_limy \) $BR Enter the values as a vector of the form: \( < y($lower_limy),y($lower_limy_p1), ... , y($upper_limy) \)> $BR \{ Real($n2) >ans_rule(60)\} \{ AnswerFormatHelp("vectors") \} $BR $PAR (c) rewrite \( x(n) \) in terms of \( \delta(n) \) and \( u(n) \) $BR Use delta(n) for \( \delta(n) \) $BR \(x(n)= \) \{ Real($n3) >ans_rule(80)\} \{ AnswerFormatHelp("formulas") \} END_TEXT Context()>normalStrings; ############################################################## # # Answers # # #ANS($xvalues>cmp(ordered=>1)); ANS(Vector(@x)>cmp()); ANS(Vector(@y)>cmp()); ANS(Real(Formula($f3))>cmp()); ENDDOCUMENT();
u()
is undefined. Note that adding a function to a Context does not make it available to use in Perl expressions, only in strings passed to MathObject constructors like Compute()
. Because you are wanting to use u()
as a function in Perl, you need to give it a definition in Perl. Something like
sub u {Parser::Legacy::Numeric::do_step(@_)}should do it. (Note that your
kdelta
function does something like that in order to get kdelta()
defined for use in Perl directly.)
While I'm here, should note that there are several other issues in your code. Some things I see right away are:
 The statement
ANS(Real(Formula($f3))>cmp());
should beANS($f3>cmp);
since$f3
is already a formula, and can't be turned into a Real (doing so just returns the formula again).
Your vectors aren't in Vector context, so your students won't be able to enter them (the angle brackets aren't defined in the Complex context.
I'm not sure why you are in Complex context anyway, since nothing here is complex. Perhaps you just want to be able to process complex answers if the students type them?
The TeX code for your vectors would be better if you used
\langle
and \rangle
rather than <
and >
, as these will format better. (You could have used MathObject to format that for you, I suppose.)
There is a
\begin{cases}...\end{cases}
environment that could be used to format the expression for x() more easily.
Hope that helps.
2) I added the Context("Vector") right before the BEGIN_TEXT. I had it n earlier and checked the input but took it out later for some reason and didn't recheck the input. 3) granted but so many of the DSP problems are complex  originally I thought I could have the answer as an ordered list. But I couldn't figure out how to convert the array x to a list, so I did Vector and changed at the point where I needed it for input.
4)I tried this but it seems using the < rather than \langle makes it easier for the students to know what to type.
5) thanks  I'll check that
I'm having a problem with the formula in the 3rd answer. So I tried to compute the values using a couple of different formulas, $f3 and $f4. When I tried to evaluate the formulas, I get the error
ERRORS from evaluating PG file: Can't take u of 0 at line 120 of (eval 2543)
120 for ($k = $n0;$k<=($n3);$k++) {$xcheck1[$k] = $f3 > eval(n =>$k);}What's wrong here? If I have the formula right, then I could use it to compute the values of x(n) more easily than either the double for loop that I use now, or the cases.
Here is the code  I REALLY appreciate the help.
DOCUMENT(); # modified to use j for i = sqrt(1) $complexJ =1; # 0 => i, 1 => j $I = ($complexJ)? 'j': 'i'; loadMacros( "PGstandard.pl", # Standard macros for PG language "MathObjects.pl", "AnswerFormatHelp.pl", ($complexJ) ? "contextComplexJ.pl" : "", #"source.pl", # allows code to be displayed on certain sites. "PGcourse.pl", # Customization file for the course ); # DTFT of aperiodic series geometric series TEXT(beginproblem()); # Show which answers are correct and which ones are incorrect $showPartialCorrectAnswers = 1; ############################################################## # # Setup # # Context("Complex"); Context()>variables>add(n=> 'Real'); Context()>variables>remove(z); # only variables should be n Context()>functions>add( u => { class => 'Parser::Legacy::Numeric', perl => 'Parser::Legacy::Numeric::do_step' }, ); # tried this and include ParserFunction.pl #parserFunction("kdelta(n)" => "u(n+0.1)u(n0.1)"); #kronecker delta approximation # this works if the student and answer checker evaluates functions at integers # use kdelta defined by LaRose package NewFunc; # this next line makes the function a # function from reals to reals our @ISA = qw(Parser::Function::numeric); sub kdelta { shift; my $x = shift; return ($x == 0 ? 1 :0); } # end subroutine package main; # Make it work on formulas as well as numbers sub kdelta {Parser::Function>call('kdelta',@_)} sub u {Parser::Legacy::Numeric::do_step(@_)} # Add the new functions to the Context Context()>functions>add( kdelta => {class => 'NewFunc', TeX => '\delta'}, ); $a = non_zero_random(2,2,1); # nonzero offset $n0 = random(6,3,1); # lower_limit $n0inc = random(3,5,1); # range of formula 1 $n1 = $n0 + $n0inc; # upper limit of formula 1 $n2 = random($n1+1,$n1+5,1); # lower_limit of formula 2 $n2inc = random(3,5,1); # range of formula 2 $n3 = $n2 + $n2inc; # upper limit of formula 2 $lower_lim = $n01; $upper_lim = $n3+1; $lower_lim_p1 = $lower_lim +1; # need for text index $D = random(2,8,1); # denominator of fraction in f1 $Nx = $upper_lim  $lower_lim +1; # number of samples for x(n) # compute limits for y(n) = x(n+n0) $n1y = $n3 +$n0; # should be negative $n3y = $n1 + $n0; #should be positive $maxlim = max($n1y,$n3y); # make limits symmetric $lower_limy = $maxlim; $upper_limy = $maxlim; $lower_limy_p1 = $lower_limy +1; # need for text index $Ny = $upper_limy  $lower_limy +1; # number of samples for y(n) $f1 = Formula("$a+ n/$D")>reduce; # formula 1 $f2 = Formula("1")>reduce; # formula 2 $f3 = Formula("($a+n/$D)*(u(n$n0)u(n$n11)) +u(n$n2)u(n$n31)")>reduce; #answer $f3>{test_points} = [[$n01], [$n0], [$n0+1], [$n11], [$n1], [$n1+1],[$n21], [$n2], [$n2+1],[$n31], [$n0], [$n3+1]]; # alternative answer $f4 = Formula("($a+n/$D)*(u(n$n0)u(n$n11)) +kdelta(n$n2)+kdelta(n$n2+1)+kdelta(n$n2+2)+kdelta(n$n2+3)+kdelta(n$n2+4)")>reduce; $f4>{test_points} = [[$n01], [$n0], [$n0+1], [$n11], [$n1], [$n1+1],[$n21], [$n2], [$n2+1],[$n31], [$n0], [$n3+1]]; for ($k = $n0;$k<=($n3);$k++) {$xcheck1[$k] = $f3 > eval(n =>$k);} #for ($k = $n0;$k<=($n3);$k++) {$xcheck2[$k] = $f4 > eval(n =>$k);} # Compute x(n) for ($k = 0;$k<=$Nx1;$k++) {$x[$k]=0;} for ($k = 1;$k<=($n0inc+1);$k++) {$x[$k] = $f1 > eval(n =>($n0+$k1));} for ($k = 1+$n2$n0;$k<=($n2inc+$n2$n0+1);$k++) {$x[$k] = 1;} # compute y(n) $yindx0 = $upper_limy; $yindx_start = $yindx0  $upper_lim+$n0; for ($k = 0;$k<=$Ny1;$k++) {$y[$k]=0;} for ($k = 0;$k<=$Nx1;$k++) {$y[$yindx_start+$k] = $x[$Nx1 $k];} for ($k = 0;$k<=3;$k++) {$d[$k]=kdelta(2$k);} for ($k = 2;$k<=2;$k++) {$ut[$k+2]=u($k);} $u0 = u(0); ############################################################## # # Text # # Context("Vector"); Context()>texStrings; BEGIN_TEXT This problem is related to 2.1 in the text. $PAR Given the discretetime signal, $n0, $n1, $n2, $n3, $d[1], $d[2], $d[3], $ut[0], $ut[1], $ut[2], $ut[3], $ut[4], $u0 \[ x(n) = \left\lbrace \begin{array}{ l l } $f1 & \mbox{ if } $n0 \leq n \leq $n1 \\ 1 & \mbox{ if } $n2 \leq n \leq $n3 \\ 0 & \mbox{ else. } \end{array} \right. \] $PAR (a) determine the values for \( $lower_lim \leq n \leq $upper_lim \) $BR Enter the values as a vector of the form: \( < x($lower_lim),x($lower_lim_p1), ... , x($upper_lim) \)> $BR \{ ans_rule(60)\} \{ AnswerFormatHelp("vectors") \} $BR (b) for \( y(n) = x(n+$n0) \), determine the values for \( $lower_limy \leq n \leq $upper_limy \) $BR Enter the values as a vector of the form: \( \langle y($lower_limy),y($lower_limy_p1), ... , y($upper_limy) \rangle \) $BR \{ Real($n2) >ans_rule(60)\} \{ AnswerFormatHelp("vectors") \} $BR $PAR (c) rewrite \( x(n) \) in terms of \( \delta(n) \) and \( u(n) \) $BR Use delta(n) for \( \delta(n) \) $BR \(x(n)= \) \{ Real($n3) >ans_rule(80)\} \{ AnswerFormatHelp("formulas") \} END_TEXT Context()>normalStrings; ############################################################## # # Answers # # #ANS($xvalues>cmp(ordered=>1)); ANS(Vector(@x)>cmp()); ANS(Vector(@y)>cmp()); ANS($f3>cmp()); ENDDOCUMENT();
u()
to the context properly. I noticed that yesterday, but decided not to go into it since you weren't using eval()
at the time, and it would complicate my answer.
Functions in MathObjets are a bit complicated. There are different classes of functions (realvalued with one real input, realvalud with two real inputs, complexvalued with complex inputs, and so on). The class
property in the function definition in the Context refers to a class that implements one of those categories of functions, and it is set up to check the parameters (both number and type) for that class. Within that class, there are methods for each of the functions of that class. So in Parser::Function::numeric
, which implements singleinput realvalued functions, there are methods for sin(x)
, ln(x)
, etc.
When you add a function like
Context()>functions>add(sin => {class => "Parser::Function::numeric"});That means that ties the
sin()
function to the method named sin
in the class Parser::Function::numeric
. When a MathObject Formula uses sin(x)
and you call it's eval()
method, the sin
method of Parser::Function::numeric
is called to determine the its value.
In your case, you did
Context()>functions>add( u => { class => 'Parser::Legacy::Numeric', perl => 'Parser::Legacy::Numeric::do_step' }, );which ties
u()
to the class Parser::Legacy::Numeric
. That class is defined in pg/lib/Parser/Legacy/Numeric.pm
, which does
Context()>functions>add( $context>functions>add( step => {class => 'Parser::Legacy::Numeric', perl => 'Parser::Legacy::Numeric::do_step'}, fact => {class => 'Parser::Legacy::Numeric', perl => 'Parser::Legacy::Numeric::do_fact'}, ); );I assume this is where you took your definition from. Note, however, that the function name was
step
in that case, so the method of Parser::Legacy::Numeric
that is called is named step
. When you replace the name step
with u
that changes the method that will be called to u
as well. But Parser::Legacy::Numeric
doesn't have a method called u
, and so the attempt to evaluate it fails. MathObjects traps the error and reports the generic message that it can't evaluate u()
at the given input.
So to do this properly, you need to use a class that actually has a method u
. You can't add u
to Parser::Legacy::Numeric
(or rather you shouldn't, since it is global to the httpd child process that evaluated your problem, and changes you make in your problem would be carried over to the next one that the child handles). So you need to make your own subclass and add u
to that.
Fortunately, you already have an appropriate class since you already have one for the kdelta
function, and u()
is a function of the same category (realvalued with one real input). So you can add u
to that class and tie your Context function to that class.
package NewFunc; # this next line makes the function a function from reals to reals our @ISA = ("Parser::Function::numeric"); sub kdelta { shift; my $x = shift; return ($x == 0 ? 1 :0); } sub u { shift; my $x = shift; return ($x >= 0 ? 1 : 0); } package main; # Make it work in Perl code sub kdelta {Parser::Function>call('kdelta',@_)} sub u {Parser::Function>call('u',@_)} # Add the new functions to the Context Context()>functions>add( kdelta => {class => 'NewFunc', TeX => '\delta'}, u => {class => 'NewFunc'} );(Make use you remove the earlier definition for
u
).
Once you do this, you should be able to both call u(x)
from within your Perl code, and be able to use eval()
on formulas that include u()
.
You might ask how the answer checker can evaluate the function if veal()
wasn't working. This is because the answer checker doesn't actually use eval()
. Instead, it converts the formula into a Perl expression directly (using the perl
method), and uses that to evaluate the function. This is more efficient if you need to evaluate the function at multiple points, which is why it is done that way. Because your original definition for u
included a perl
definition, the translation to perl used that rather than the default call to the u
method in the Parser::Legacy::Numeric
class. So answer checking worked as expected.
Hope that clarifies the situation.
You could have used
ANS(List(@x)>cmp(ordered=>1));to get your ordered list rather than use a vector. I think that is probably a better solution.
u()
, you will get a new error from your problem. In this case, it will complain about using an array index that is 6 (or some negative number depending on your seed). That came from
for ($k = $n0;$k eval(n =>$k);}since
$n0
was 6 for me. You probably need $xcheck1[$k$n0]
instead.
On the other hand, it might be easier to use
push(@xcheck1,$f3>eval(n => $k));instead, and not have to worry about the indices for the array. In fact, you could do
foreach $k ($n0..$n3) {push(@xcheck1,$f3>eval(n=>$k)}to simplify the for loop, or
@xcheck1 = map {$f3>eval(n => $_)} ($n0..$n3);to make it even easier.
This function is available by default for some problems, and may or may not be enabled for others (for authoring information see step functions):
 step(x) The step function (0 if x < 0, 1 if x is greater than or equal to 0.)
Yes, the page is not consistent with the code.
so I should use parserFunction("u(n)" => "1step(n)");
Note that this requires step()
to be defined in the context, which is not the case for the Numeric context. So you could either use the LegacyNumeric context (which also defines frac()
, and corresponds to the functions defined in the original num_cmp()
answer checker), or import the step function from that context:
Context("Numeric"); Context()>functions>redefine("step",from=>"LegacyNumeric"); parserFunction("u(n)" => "1step(n)");Note that this means
step()
will also be available to students in their answers, but it is uniquely anyone will try that. If you want to prevent it, then use the mechanism of defining u()
that I described above.
Hope that does the trick.