Maple remembers too much

Doug Meade's picture

The following example was shown to me by Matthias Kawski.

He noticed a difference in the way Maple 12 handles some definite integrals from the way they were done in Maple 8. Both cases point out some problems. I'll offer my explanation of the problem after I show what Matthias observed: First, the results from Maple 8. Each of these 4 examples evaluates a definite integral in two different ways, the first using a function and the second using an expression. All looks fine until the last example.

 

----------------------------------------------------------

MAPLE 8

> restart;
> f:=t->sin(t)
> int(f,0..Pi);
> int(f(x),x=0..Pi);

2

2

> f:=t->sin(3*t):
> int(f,0..Pi);
> int(f(x),x=0..Pi);

2/3

2/3

> f:=t->sin(Pi*t):
> int(f,0..1);
> int(f(x),x=0..1);

2
----
Pi

2
----
Pi

> f:=t->sin(3*Pi*t):
> int(f,0..1);
> int(f(x),x=0..1);

2
----
Pi

2
----
3 Pi

----------------------------------------------------------

Now, the same examples in Maple 12. Now there are problems in the second and fourth examples.

----------------------------------------------------------

MAPLE 12

restart;
f:=t->sin(t):
int(f,0..Pi);
int(f(x),x=0..Pi);

2

2

f:=t->sin(3*t):
int(f,0..Pi);
int(f(x),x=0..Pi);

2

2
-
3

f:=t->sin(Pi*t):
int(f,0..1);
int(f(x),x=0..1);

2
--
Pi

2
--
Pi

f:=t->sin(3*Pi*t):
int(f,0..1);
int(f(x),x=0..1);

2
--
Pi

2
----
3 Pi

----------------------------------------------------------

These are not difficult integrals. Why does Maple have so much trouble with the integrands that are expressed as functions and not when the integrand is an expression?

I believe the problem is in the remember table, and the way in which the functional arguments are handled. The problem disappears if each function has a different name. It also disappears if each example starts with a restart;.

When Maple sees f(x), it evaluates to the function definition. Each of the the f(x) integrands looks different to Maple, so it evaluates each one when it is encountered. But, when the integrand is passed as a function name, f, that's all that Maple sees. After the first one, the result is placed into Maple's remember table. The subsequent calls see that this result has already been computed and simply returns that result.

This is not the complete story, as some of the results are different when the order of the tests is changed. In spite of this, I stand by my diagnosis that this has something to do with the way a function name is not expanded and the use of remember tables.

Further explanations, and corrections, are appreciated.

Doug

acer's picture

last-name-eval

Sure, last-name-eval and remember tables are known to be a risky mix.

I'd be mildly surprised if something couldn't be changed to allow it to work better in `int`, though.

There is a remember table in use by `int/int`. That routine has options `remember` and `system`. That means that, while it will remember arguments, it's memory can be cleared during garbage collection. That probably explains why you got slightly different results in some layouts -- because gc was happening and "fixing" things by forgetting some of the undesirable last-name-eval proc name entries from the remember table.

Maple 8 can get examples wrong too. (It's not the case that `int/int` did not have option `remember` in Maple 8. It did.) For example,

> kernelopts(version);
          Maple 8.00, SUN SPARC SOLARIS, Apr 22 2002 Build ID 110847
 
> f := x->2*x:
> int( f, 0..1 );
                                       1
 
> f := x->3*x:
> int( f, 0..1 );
                                       1
 
> restart:
> f := x->3*x:
> int( f, 0..1 );
                                      3/2

acer

garbage collection

seems indeed to be a workaround. Reexecuting in Maple 12, with some 'gc()' added, without a restart:

> gc():f:=t->sin(t):
> int(f,0..Pi);
> int(f(x),x=0..Pi);
                                  2


                                  2

> gc():f:=t->sin(3*t):
> int(f,0..Pi);
> int(f(x),x=0..Pi);

                                 2/3


                                 2/3

> gc():f:=t->sin(Pi*t):
> int(f,0..1);
> int(f(x),x=0..1);

                                  2
                                 ----
                                  Pi


                                  2
                                 ----
                                  Pi

> gc():f:=t->sin(3*Pi*t):
> int(f,0..1);
> int(f(x),x=0..1);

                                  2
                                 ----
                                 3 Pi


                                  2
                                 ----
                                 3 Pi


jpmay's picture

forget

forget(int);  would be more appropriate here.   `int/int` has the option 'system' which means:

Option system serves to identify procedures in the Maple library that are
  considered to be "system functions", meaning functions for which the
  remember table may be deleted at garbage collection time. If this option is
  not specified for a procedure that has the remember option, the remember
  table survives garbage collections.

So gc() isn't necessarily guaranteed to work here.

John

Doug Meade's picture

automatic selective forget

I had forgotten about forget. Thanks for pointing it out to me.

But, to be truly effective, what I need is a version of forget that allows me to forget everything in any remember table whenever f is redefined. This would require an ability to determine every remember table that contains entries involving f and removing only those entries from the remember table.

I don't have the expertise with remember tables to implement this right now. Is this realistic? Is it something that should be suggested to the developers for a future release?

Doug

---------------------------------------------------------------------
Douglas B. Meade  <><
Math, USC, Columbia, SC 29208  E-mail: mailto:meade@math.sc.edu       
Phone:  (803) 777-6183         URL:    http://www.math.sc.ed
jpmay's picture

Selective forget

It is already kind of possible to do that:

f(x) := 456:f(y) := 12:  f(x), f(y);

forget(f,x); f(x), f(y);

But it is quite limited.  If f has multiple arguments, you need to specify all of them; it doesn't use wildcards.  forget also cannot be called on all functions with remember tables since that information is not stored centrally: each defined function would have to be individually inspected.

However, as acer pointed out, the deeper issue here is that remembers tables should not be used on obects with last name evaluation.  That is,  we shouldn't need to have such a selective 'forget' at all.

That with problem int and functions should be fixed in the next release of Maple.

John

acer's picture

operator form

This is partly related. The `int` routine is not as smart as `D`, for handling operators. It evaluates the procedure at some name (_X, say). But that may not result in what was intended.

For example,

> restart:

> f := proc(x)
>    if is(x>0)=true then x; else Pi; end if;
> end proc:

> int(f, 1..2); # oops
                                      Pi
 
> forget(`int/int`):
> int(f, a..b) assuming a>0, b>a; # oops
                                  Pi (b - a)
 
> D[1](f); # good
                                    D[1](f)
 
> D[1](f)(1); # good
                                    D(f)(1)
 
> evalf(%); # right
                                  1.000000000
 
> D[1](f)(-1); # good
                                   D(f)(-1)
 
> evalf(%); # right
                                      0.

Here's another example,

> restart:

> h := proc(x) sign(x); end proc:

> int(h, -1..0);
                                       1
 
> evalf(Int(h, -1..0));
                                 -1.000000000

acer

Axel Vogt's picture

is there really no other way?

is there really no other reliable way to clean up everything then to close and restart the complete sheet?

jpmay's picture

forget all

In theory, I guess one could make a forget all function doing something like:

for p in {anames('procedure')} do
      forget(p);
end do;

Things get more fiddly when one wants to make sure any remember tables of functions stored in modules are also cleared (especially if they are local module members).  But it should be possible. All the extra fiddly stuff is probably why we don't have such a function already.

John

great minds

jinx!

forget

Maybe the output from anames() could provide the prefixes for a utility to do this. But it would have to walk over modules.

It looks like forget hasn't been updated to handle all module members as "subfunctions", so as to be able to clear them "recursively".

> restart:

> p := module()
> export f;
> local g;
> f := proc(x) g(x); end proc;
> g := proc(x) option remember; time(); end proc:
> end module:

> p:-f(3);
                                     0.017
 
> kernelopts(opaquemodules=false):
> op(4,eval(p:-g));
                              table([3 = 0.017])
 
> int(cos(sin(x)),x):
 
> time(),p:-f(3);
                                 0.197, 0.017
 
> forget(p);

> op(4,eval(p:-g));
                              table([3 = 0.017])
 
> time(),p:-f(3);
                                 0.199, 0.017

The help page for forget is interesting, because it refers to packages so old that they might predate the / naming convention for "subfunctions".

     - The function forget may not work with some procedure
       defined in packages, because it relies on a heuristic
       naming convention that is not adhered to in some,
       older packages.

Of course, now there are newer packages, implemented as modules, which also don't adhere to that convention.

so old packages

Yes, I was reading that statement. Which are those so old packages?

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
}