WeBWorK Main Forum

Context trouble - trying to add an operator

Context trouble - trying to add an operator

by Wesley Cramer -
Number of replies: 12
I've been trying to make A and V be operators. Everything else appears to be working fine, but when I add the lines


$context->operators->clear();
$context->operators->set("A"=>{string=>"A",TeX=>"\wedge ",perl=>"*"});
$context->operators->set("V"=>{string=>"V",TeX=>"\vee ",perl=>"+"});

to my problem I get the following error

"an't locate object method "weaken" via package "main" at [PG]/lib/Parser/BOP.pm line 33 Died within main::Formula called at line 93 of [PG]/macros/Parser.pl from within main::Compute called at line 33 of [TMPL]/1.5/StatementEval.pg "

I've also tried using

$context->operators->set(add....

but that gave me another error. Any help would be greatly appreciated. Here's the full code for the problem:

DOCUMENT();
loadMacros(
"PGstandard.pl",
"MathObjects.pl", "answerCustom.pl", "contextLogic.pl", "symlogic.pl"
);
TEXT(beginproblem());

$context = Context("Numeric");
$context->variables->clear();
$context->variables->add(p=>'Real');
$context->variables->add(q=>'Real');
$context->variables->{patterns} = {'[^AaVv]'};
$context->operators->clear();
$context->operators->set("A"=>{string=>"A",TeX=>"\wedge ",perl=>"*"});
$context->operators->set("V"=>{string=>"V",TeX=>"\vee ",perl=>"+"});
#$context->operators->set("~"=>{string=>"~",TeX=>"\sim",perl=>"-"},type=>"unary");
#$context->strings->{patterns}{'^.*$'} = [-20,'str'];
$context->update;

$ans = 'pAq';
$val = cos($ans);

BEGIN_TEXT
Enter a value of \(x\) for which \(\cos(x) = $val\):
\($ans = \) \{ ans_rule(25) \}
END_TEXT

$statementChecker = sub {
my ($correct, $student, $ans ) = @_;
return statementChecker($correct, $student, $ans);
};

ANS( Compute($ans)->cmp( checker=>$statementChecker) );

ENDDOCUMENT();
In reply to Wesley Cramer

Re: Context trouble - trying to add an operator

by Wesley Cramer -
As a work around I'm taking everything in as strings without any parsing by webwork and providing my own error checking and answer checking, but I would still like a way to format the answer preview. Does anyone know of some way to tell webwork what to put as the preview?
In reply to Wesley Cramer

Re: Context trouble - trying to add an operator

by Davide Cervone -
The problem with your operators is that you have not given all the required attributes. In particular, you have not given them any implementation. It is not enough to just give string, TeX, and perl values; you also need precedence, associativity, type, and most important, a class that instantiates the operator in the parse tree. It is the lack of a class that has caused the error you see.

Here is one way to accomplish what you want. The basic idea is to copy the operators from the standard definitions for *, + and unary -, and then change their string and TeX representations. That will get you the implementation details without needing to know them.

It is a little bit awkward, however, because we need ~ to be mapped to the unary minus, which is a 'hidden' operator in the Numeric context (the minus is both a unary and binary minus, and the unary one is chosen when needed, but it's name is not used as one of the operators that students can enter, which makes it 'hidden').

To get around this, we make a copy of the unary minus, then change its hidden status then copy that to the ~ operator. Finally we remove the unwanted copy. That is all made necessary due to a quirk in when 'hidden' has its effect (it is when the operator is first created, so changing it after the fact doesn't help. It took me a while to figure out what was happening.)

    $context = Context("Numeric");
    $context->variables->clear();
    $context->variables->add(p=>'Real');
    $context->variables->add(q=>'Real');
    $context->operators->clear();
    $context->operators->redefine('u-',using=>'u-',from=>'Numeric');
    $context->operators->set('u-'=>{hidden=>0});
    $context->operators->redefine('A',using=>'*',from=>'Numeric');
    $context->operators->redefine('V',using=>'+',from=>'Numeric');
    $context->operators->redefine('~',using=>'u-',from=>$context);
    $context->operators->set(
      A => {string=>"A",TeX=>"\wedge ", perl=>"*"},
      V => {string=>"V",TeX=>"\vee ", perl=>"+"},
      "~" => {string=>"~",TeX=>"\sim ", perl=>"-"}
    );
    $context->operators->remove('u-');
Note that you should not change the {patterns} since that is set automatically as values are added and removed. You can change {namePattern} if you want to be allowed to create objects with different names than are allowed by default. If you are trying to change the error messages, there are other ways to handle that that are better.

Finally, you should not need to call $context->update since that is done automatically when something changes via set, remove, etc.

I might recommend making ^ be wedge rather than A, but that is up to you.

Hope that helps.

Davide

In reply to Davide Cervone

Re: Context trouble - trying to add an operator

by Wesley Cramer -
Thank you, that helped a lot! Unfortunately, I've run into another problem.

Often times with the operations *, + and ** ()'s are not needed. However because all logical expressions (except not) have the same precedence, ()'s are always needed. Setting the precedence equal for "and" "or" and "implies" actually makes matter worse. WeBWorK then ignores all ()'s. Is there a way to protect them?

There is a similar issue with the fact that "implies" is non-associative, and therefore statements like p->q->r are ambiguous. I tried associativity=>"non", but this didn't seem to make a difference.

Ideally I would like a way to protect ()'s and still accept things like ~p->q or be able to provide my function for preview of student answers.
In reply to Wesley Cramer

Re: Context trouble - trying to add an operator

by Davide Cervone -
Unfortunately, the parentheses are not as flexible as you might want. There are a couple of approaches to forcing more parentheses to show.

The first is to use

    $context = Context();
    $context->flags->set(showExtraParens=>2);
    $context->operators->set(
      '+' => {fullparens => 1},
      '*' => {fullparens => 1},
      '**' => {fullparens => 1},
    );
This will force parens around +, *, and ** operations whenever they appear as an operand of some other operator. E.g., x+y+z will show as (x+y)+z, and xy+z will be (x*y)+z.

An alternative is

    $context = Context();
    $context->operators->set(
      '+' => {leftparens => "all", rightparens=>"all"},
      '*' => {leftparens => "all", rightparens=>"all"},
      '**' => {leftparens => "all", rightparens=>"all"},
    );
which forces the left-operand of +, *, and ** to be parenthesized if it contains any other operation, and similarly for the righ-hand operand.

These two approaches are similar, but not quite the same. The first tells when to put parens around an expression containing +, *, or **, and the second tells when to force parens within the operands of +, *, and **.

Note that neither of these "protects" parentheses from your original expression. This forces parens in certain situations, regardless of whether they were there originally or not.

Hope that makes sense.

Davide

In reply to Davide Cervone

Re: Context trouble - trying to add an operator

by Wesley Cramer -
I might be implementing your solution incorrectly, so here's what I have

$context = Context("Numeric");
$context->variables->clear();
$context->variables->add(p=>'Real');
$context->variables->add(q=>'Real');
$context->variables->add(r=>'Real');
$context->operators->clear();
$context->operators->redefine('u-',using=>'u-',from=>'Numeric');
$context->operators->set('u-'=>{hidden=>0});
$context->operators->redefine('A',using=>'*',from=>'Numeric');
$context->operators->redefine('V',using=>'+',from=>'Numeric');
$context->operators->redefine('->',using=>'**',from=>'Numeric');
$context->operators->redefine('~',using=>'u-',from=>$context);
$context->flags->set(showExtraParens=>2);
$context->operators->set(
A => {leftparens => "all", rightparens=>"all",string=>"A",TeX=>"\wedge ", perl=>"*"},
V => {leftparens => "all", rightparens=>"all",string=>"V",TeX=>"\vee ", perl=>"+"},
"->" => {leftparens => "all", rightparens=>"all",string=>"->",TeX=>"\to ", perl=>"**"},
"~" => {string=>"~",TeX=>"\sim ", perl=>"-"}
);
$context->operators->remove('u-');

And here's the output when I try check answers

[(p->q)A[~r->(~q)]]->(r->p) \left(p\to {q}\right)\wedge \left(\sim r\to {\sim q}\right)\to {r\to {p}} incorrect Can't generate enough valid points for comparison

If I have the webwork parser treat the answer as a string, the preview is no good but the answer evaluator works correctly.
In reply to Wesley Cramer

Re: Context trouble - trying to add an operator

by Wesley Cramer -
As an update for anyone else who might be trying something similar. Here's what ended up working


$context = Context("Numeric");
$context->variables->clear();
$context->variables->add(p=>'Real');
$context->variables->add(q=>'Real');
$context->variables->add(r=>'Real');
$context->operators->clear();
$context->operators->redefine('u-',using=>'u-',from=>'Numeric');
$context->operators->set('u-'=>{hidden=>0});
$context->operators->redefine('A',using=>'*',from=>'Numeric');
$context->operators->redefine('V',using=>'+',from=>'Numeric');
$context->operators->redefine('->',using=>'**',from=>'Numeric');
$context->operators->redefine('~',using=>'u-',from=>$context);
$context->flags->set(showExtraParens=>1);
$context->operators->set(
A => {fullparens => 1,string=>"A",TeX=>"\wedge ", perl=>"*"},
V => {fullparens => 1,string=>"V",TeX=>"\vee ", perl=>"+"},
"->" => {fullparens => 1, associativity => "non", string=>"->",TeX=>"\to ", perl=>"**"},
"~" => {precedence => 9.5, string=>"~",TeX=>"\sim ", perl=>"-"}
);
$context->operators->remove('u-');

The error message I was getting before was some bad code in my checker. Davide - THANK YOU for your help. You saved me many hours!
In reply to Wesley Cramer

Re: Context trouble - trying to add an operator

by Davide Cervone -
Note that associativity=>"non" has no meaning in MathObjects. The only valid associativities are "left" and "right", which tell whether

a op b op c

is interpreted as

(a op b) op c

or

a op (b op c).

I'm not sure what the consequence of using an invalid associativity will be (it may be inconsistent), but you should not use it.

The precedence setting is probably what changed the situation for you, but I would have expected it to prevent the expression from having any valid points.

Glad it is working, but see my other message for more.

Davide
In reply to Davide Cervone

Re: Context trouble - trying to add an operator

by Wesley Cramer -
I am using a custom checker to tell whether the student input is equivalent to the correct answer. Basically I create a sign chart for both and compare. Before when I got that error I had inadvertently switched off the custom answer checker.

I will get rid of the associativity assignment though. Thanks!
In reply to Wesley Cramer

Re: Context trouble - trying to add an operator

by Wesley Cramer -
Also, for those that are writing logic problems here's some useful functions I've created. Feel free to write me if you have any questions.
In reply to Wesley Cramer

Re: Context trouble - trying to add an operator

by Davide Cervone -
OK, thanks for posting this. It looks like you have most of what it would take to make a full logic context already. It is a matter of integrating it with the MathObject operators. At some point, we should do that (but I don't have the time to go through that with you at the moment). I didn't realize you were replacing the checker as well; after I had written you yesterday, I though about the idea of applying all possible T/F inputs in order to do an equivalence check as well, so it should be pretty straight forward to make a MathObject context for this, and avoid all the hassle you have been experiencing.

Davide
In reply to Davide Cervone

Re: Context trouble - trying to add an operator

by Wesley Cramer -
That would be great. Much nicer then adding all that code to each problem. I also don't have time to work on it right now though. By the way there was a small error in the code I posted. Here's the fixed version.
In reply to Wesley Cramer

Re: Context trouble - trying to add an operator

by Davide Cervone -
Well, the problem is that the expression that WeBWorK is actually evaluating is

[(p**q)+[-(r**(-q))]]^(r**q)

where p, q, and r are all real numbers in the range -2 to 2. Unfortunately, exponentiation isn't defined for negate bases and non-integer powers, so you are getting errors generating the points to test this on.

Note that, due to the rules of precendece, you aren't getting the (-r)**(-q) that I think you mean it to be (since -x**2 means -(x^2) not (-x)**2). If you had gotten that, then you would have both -r and r as bases for exponents, and it woudl be very difficult to obtain any points where the expression could be evaluated.

I am wondering if you could use some other non-commutative operator, like division or subtraction, instead. You may also want to set the precedence values so that ~p -> ~q will work as well.

I'm not sure whether this is going to end up being the best approach to these problems. It is not clear to me whether logically equivalent statements will be counted as equal in this setup. For example, will ~(p->q) be equal to pA~q? If p=2 and q=1, then the first will equal -(2**1),or -2, while the second will be 2+(-1), or 1, so no, it seems not.

In that case, you are really checking for the exact expression, up to some commutativity of wedge and vee. In that case, it might be better to implement an actual logic context that has a better idea of equality of these expressions (up to the transformations you care about, like commutativity). That could be done by a formal structural comparison rather than a numeric evaluation. This is a more complicated prospect, as you would need to write the actual implementations of the operators, but it is probably more reliable than this rather ugly hack. This would also let you handle the parentheses more to your liking. I just think you are going to continue to find things that don't work as you want them to.

Davide