WeBWorK Problems

Numerical Quadrature Formulas

Numerical Quadrature Formulas

by Steven McKay -
Number of replies: 6
Hi,
I have been relearning how to code problems using Mathobjects, and things are going fine. However, I am at the point where I need to enter questions about Trapezoid, Midpoint and Simpson's. My goal is to have questions on the webwork side that are as similar as possible to the paper homework that is currently assigned in Calculus II. That way, the transition to online homework will be easier (I hope).

I am now at the section that has the numerical quadrature formulas. I have looked at the questions in the Library pertaining to numerical quadrature, and they seem to have the formulas hard coded: n is fixed and they calculate trap, mid, or simp by hand, basically. It seems to me that it should not be hard to code routines that will do these calculations automatically, thus making it easier to write questions in this area.

My first question is: has anyone written these with Mathobjects in mind? I know there is some old Romberg code, but as far as I can tell, it has not been updated to work with Mathobjects.

If the answer to the above is yes, where do I access them?

If the answer to the above is no, then my second question is: what would be the easiest way to get them written? Note I did not say the best way, as I am under time pressure here. It would be nice if the easiest way was the best way. smile

I think the best way would be to incorporate them as Formula methods, like differentiation is now. I don't know if that's possible to do without rewriting parser.

So, I guess I'm asking how to pass Formulas to a function. I have looked at the code snippet "adding functions to a context" and I think I understand it, but I don't really do Perl. (I've done Fortran, C and C++, which, in my opinion are a lot easier to read than perl.smile)

I really don't understand what
sub log2 {Parser::Function->call('log2',@_)}

does either.

Thanks for any help you have, and if there is a good example
of this elsewhere and I missed it, I apologize in advance.

S.M.

In reply to Steven McKay

Re: Numerical Quadrature Formulas

by Davide Cervone -
I think the best way would be to incorporate them as Formula methods, like differentiation is now. I don't know if that's possible to do without rewriting parser.

That would not be the best approach, for technical reasons (your code would be modifying the persistent code that is held by the mod_perl child process, and that is not a good idea).

But you could create a subclass of the Formula object and add your methods to that:

    package myFormula;
    our @ISA = ('Value::Formula');

    sub method1 {
      ...
    }

    sub method2 {
      ...
    }

    package main;
    sub myFormula {new MyFormula(@_)}

and then use

    $f = myFormula("...");
    $f->method1(...);
    $f->method2(...);

to access the methods.

But I'm not sure that is the best approach for you. Your suggestion of passing a Formula to a function may be better. To do that, just pass the variable that stores it:

    sub useF {
      my $f = shift;
      my $x = shift;
      return $f->eval(x=>$x);
    }

    $f = Formula("x+1");
    $fx = useF($f,3.1);
This would pass the formula to useF() and that code calles the Formula's eval() method to compute its value at an x that was passed to it.

If you are going to be calling a Formula many times, it is inefficient to use its eval() method (which walks its parse tree by hand computing the result). Instead, first create a Perl version of the function with the perlFunction() method:

    sub computeMany {
      my ($f,$mx,$Mx,$n) = @_;
      my $F = $f->perlFunction;  # get a Perl subroutine reference
      my $dx = ($Mx-$mx)/$n;
      my @Fx;
      foreach $i (0..$n) {push(@Fx,&{$F}($mx+$i*$dx)}
      return @Fx;
    }

    $f = Formula("x^2-3");
    @Fx = computeMany($f,-2,2,10);
Here, the routine creates a Perl code reference to a subroutine that computes the value of $f more efficiently than the eval() method and uses that to compute n+1 values of f between mx and Mx.


I really don't understand what


    sub log2 {Parser::Function->call('log2',@_)}
does either.

It is a means of overloading functions that are not built into Perl so that they will work on both MathObject and regular Perl values. Ignore it.

Hope that helps.

Davide

In reply to Davide Cervone

Re: Numerical Quadrature Formulas

by Steven McKay -
Thanks Davide,

That was very helpful.

S.M.
In reply to Steven McKay

Re: Numerical Quadrature Formulas

by Michael Gage -
You can also use routines in PGnumericalmacros.pl in combination with Formulas

For example:
$F = Formula(" ");
$definite_integral = romberg($F->perlFunction, 0, 10, level=>6);

will calculate the integral of F from 0 to 10 using Romberg integration iterated 6 times.

The macros in PGnumericamacros.pl have not been worked on in a long time -- feel free to improve or add to them.

-- Mike
In reply to Michael Gage

Re: Numerical Quadrature Formulas

by Steven McKay -
Ok,

Remember I am under time pressure, so I took the easy way out.

I spent about 20 minutes and took what was already in PGnumericalmacros.pl and added routines to do what I want. Since trapezoid is already there (needed for Romberg), I just took that code and modified it slightly to get the left hand sum, right hand sum, Midpoint and Simpsons. Right now I have these in a file I call PGnumericalextra.pl.

I have two questions.

First, it seems to me these routines might be useful to others - it seems also that they would fit better in PGnumericalmacros.pl. If you agree, I'll add them to PGnumericalmacros.pl and send it to you for your inspection. If you don't agree, then I guess I'll just keep them in house :-)

Second, would it be useful to take PGnumericalmacros and rewrite it with MathObjects in mind? After thinking about it, I don't know that there is, but would like others opinions. If there is I would be willing to work on it. However, it is apparent that there would still need to be legacy code for old problems that do not rely on mathobjects. So, If the answer to the above is yes, is there a way to tell whether the passed object is a Mathobjects formula or a perl function?

Thanks,
S. McKay

In reply to Steven McKay

Re: Numerical Quadrature Formulas

by Michael Gage -
First, it seems to me these routines might be useful to others - it seems also that they would fit better in PGnumericalmacros.pl. If you agree, I'll add them to PGnumericalmacros.pl and send it to you for your inspection.

Please do this and I'll put it in the HEAD version of pg. and Thanks!!

Second, would it be useful to take PGnumericalmacros and rewrite it with MathObjects in mind? After thinking about it, I don't know that there is, but would like others opinions. If there is I would be willing to work on it. However, it is apparent that there would still need to be legacy code for old problems that do not rely on mathobjects. So, If the answer to the above is yes, is there a way to tell whether the passed object is a Mathobjects formula or a perl function?


I think there is, although it might not be super urgent. It would allow

romberg($formulaObject,.....) instead of 
romberg($formulaObject->perlFunc,....)
or perhaps eventually $formulaObject->integral(a,b)

On the second point use the ref(....) function -- it returns a string which tells you the type of the passed object, e.g. CODE (for a subroutine) or ARRAY or HASH, or in the case of MathObjects something like Value::Formula or Value::Real (I used PGlabs to check this.)


So using ref() your subroutine can determine what kind of object it is being passed. ref(string) or ref(number) or any other scalar returns the null string.

-- Mike



In reply to Michael Gage

Re: Numerical Quadrature Formulas

by Davide Cervone -
The Value package (part of MathObjects) has a function for telling you whether a variable is a reference to a MathObject or not:
    Value::isValue($f)
will return true if $f is a MathObject and false otherwise. There is also one for telling is something is a Formula object (as opposed to other MathObjects:
   Value::isFormula($f)
So you could use something like
    sub mySubroutine {
      my $f = shift; if (Value::isFormula($f)) {$f = $f->perlFunction}
      ...
    }
to convert a MathObject Formula into a subroutine reference. That way you could pass either a MathObject or a CODE reference to mySubroutine and $f would always end up being a CODE reference. You could even do
    sub mySubroutine {
      my $f = shift;
      if (ref($f) ne "CODE") {
        $f = Value->Package("Formula")->new($f)->perlFunction;
      }
      ...
    }
so that if $f is passed as a string or as a Formula or as some constant, it will be turned into a formula and converted to a CODE reference. This approach does not require that MathObjects.pl be loaded (which using main::Formula() in place of Value->Package("Formula")->new() would).

The problem with using ref($f) and looking for Value::Formula is that there are a number of MathObjects that make subclasses of Value::Formula, so unless you check for those as well, you might not handle all the formulas that people would pass to you. And since new subclasses can be made at any time, you need a better way (like Value::isFormula()).

Hope that helps.

Davide