WeBWorK Problems

add function to Complex context

add function to Complex context

by Alex Jordan -
Number of replies: 5

Is there something additional that need to be done to add a function to the Complex context? We are trying to add the cis function. I've tried defining it as cos(z)+i sin(z), as exp(iz), and more fundamental definitions, but nothing we've tried works. I suspect that it has something to do with Complex handling numbers as pairs, and I can't see how to define it correctly. Here is one version where I think I've come the closest:

package my::Function::complex;
our @ISA = ('Parser::Function::complex');
sub cis {
  my $self = shift; my ($a,$b) = $self->value;
  my $e = CORE::exp(-$b);
  my ($c,$s) = (CORE::cos($a),CORE::sin($a));
  return $self->make($e*$c,$e*$s);
}

package main;
Context("Complex");
Context()->functions->add(
cis => {class => 'my::Function::complex', TeX => '\operatorname{cis}'},
);


But when checking an answer Formula("cis(z)") this leads to the error:

Can't generate enough valid points for comparison

In reply to Alex Jordan

Re: add function to Complex context

by Davide Cervone -

The problem is you are subclassing the wrong thing. It turns out that the complex functions are computed in the Value::Complex package, not the Parser::Function package (and the same is true of real functions, vector functions, and so on in their respective Value classes). That is, cos($z) is actually computed by $z->cos() (which is why you used $self->value because it is the object calling that function that is actually the argument of the function).

So you need to put your cis function into a subclass of Value::Complex, not Parser::Function, and the class that it has is Parser::Function::complex. So how do you hook in your complex subclass, you ask? You set Context->{value}{Complex} to point to your complex class. That will make the parser create your class when a complex value is needed rather than Value::Complex. So here is an example:

package my::Complex;
our @ISA = ('Value::Complex');

sub cis {
  my $z = shift;
  return cos($z) + main::i * sin($z);
}

package main;

Context("Complex");

Context()->{value}{Complex} = 'my::Complex';
Context()->constants->set(i => {value => my::Complex->new(0,1)});
Context()->functions->add(cis => {
  class => 'Parser::Function::complex', TeX => '\operatorname{cis}',
  complex => 1, isCommand => 1
});

sub cis {Parser::Function->call("cis", my::Complex->new(@_))}

You also need to tell the parser that the return value for cis is a complex number (complex => 1) and that you need to use the internal command for calling the function rather than a native perl function when an executable version of a formula containing cis is generated (for evaluating student answers, for example) with isCommand => 1. You can remove that last if you add the sub cis that is the last line of code. That line also allows you to use cis() in your perl code directly (e.g., my $z = cis(3+i)), but is otherwise optional.

The last thing to worry about is the fact that the constant i in the Complex context is an instance of Value::Complex, so I added a line to modify the constant so that it is a my::Complex object. (I have to say, that one took me a while to realize -- I kept getting Value::Complex values when I thought they should be my::Complex.)

So that should do it for you.

In reply to Davide Cervone

Re: add function to Complex context

by Alex Jordan -

Thanks Davide,

It's working as expected (although for future readers referencing this post, some parentheses are needed after `Context`). I appreciate the full explanation. I think I had elements of this in other attempts, but I was shooting in the dark. One thing that never occurred to me was the need to use `main::i` instead of just `i` in the `cis` definition.  And I would not have known to set `Context->{value}{Complex}`.

Alex

In reply to Alex Jordan

Re: add function to Complex context

by Davide Cervone -

> some parentheses are needed after Context

That may be true in older versions of Perl, but they weren't needed in my tests, so I think it is OK as given in my post. Does it fail for you?

In reply to Davide Cervone

Re: add function to Complex context

by Alex Jordan -
Yes, it fails before I added the parens.


And then the parens clear it up.

On the server, when I check the perl version, I get 5.26.1. It looks like 5.28 and 5.30 have been out since then, but 5.26 is not too old, from 2017.

$ perl --version

This is perl 5, version 26, subversion 1 (v5.26.1) built for x86_64-linux-gnu-thread-multi
(with 67 registered patches, see perl -V for more detail)

In reply to Alex Jordan

Re: add function to Complex context

by Davide Cervone -

Interesting. I'm using 5.26.3 and don't get an error with Context->constants or any of the other ones. But perhaps that is a side-effect of the fact that I'm using the interactive lab, rather than a separate problem file. I'll put the parens back into my answer.