MathGroup Archive 1995

[Date Index] [Thread Index] [Author Index]

Search the Archive

PowerExpand/InverseTrig snafu

  • To: mathgroup at christensen.cybernetics.net
  • Subject: [mg830] PowerExpand/InverseTrig snafu
  • From: Dave Wagner <wagner at bullwinkle.cs.Colorado.EDU>
  • Date: Mon, 24 Apr 1995 02:54:14 -0400

>To: Jack Goldberg <jackgold at math.lsa.umich.edu>
>Subject: Re: Reply to my plea 
>In-reply-to: Your message of "Wed, 19 Apr 95 11:35:02 EDT."
             <Pine.SUN.3.91.950419113115.7364A-100000 at jack.math.lsa.umich.edu> 
--------

My reply to Jack Goldberg's question about introducing a new option
(InverseTrig) to a built-in function (PowerExpand) had several
problems.  I feel culpable for several of these, but one of themis
something that I think very few people could have foreseen.

First, let me point out two minor problems with the expression

	(InverseTrig /. opts /. Options[PowerExpand]) == True

The first "problem" is that the comparison is unnecessary; if a
condition evaluates to anything other than True, the rule will not fire.

The second problem is that, in general, opts should be surrounded by
list braces:

	(InverseTrig /. {opts} /. Options[PowerExpand]) == True

This will work even if there is more than one option in the command.

But the SERIOUS problem is that with a definition of this form:

	PowerExpand[expr_, opts___Rule] /;
	    (InverseTrig /. {opts} /. Options[PowerExpand]) == True :=
	Module[...
	    ...PowerExpand[expr]...
	]

you should enter an infinite recursion if you ever do a

	SetOptions[PowerExpand, InverseTrig->True]

because the option-extraction condition will always return True!
(I say "should" because, in this particular case it doesn't happen,
for reasons that will become clearer below.)

The fix to this problem in general is to use a global variable as
a flag, as in

	intercept = True;

	PowerExpand[expr_, opts___Rule] /;
	    intercept && (InverseTrig /. opts /. Options[PowerExpand]) :=
	Block[{intercept = False},
	    Module[...
		...PowerExpand[expr]...
	    ]
	]

The Block command turns off the flag temporarily so that recursive
calls are prevented.  The true advantage of using Block for this
purpose is that, even if the computation is aborted, the flag will
be reset before returning. (Thanks to Todd Gayley for this insight.)

Now, it turns out that I have opened a tremendous can of worms here, as
the following transcript will show.

    In[1]:=
	Unprotect[PowerExpand]
    Out[1]=
	{PowerExpand}

Here is a rule for PowerExpand with some strategically-placed
Print statements:

    In[2]:=
	PowerExpand[expr_, opts___Rule] /;
	    With[{it = InverseTrig /. {opts} /. Options[PowerExpand]},
		 Print["in condition, intercept = ", intercept,
		       ", InverseTrig = ", it];
		intercept && it
	    ] :=
	Block[{intercept = False},
	    Print["in body"];
	    PowerExpand[expr]
	]

    In[3]:=
	Options[PowerExpand] =
		Append[Options[PowerExpand], InverseTrig->False]
    Out[3]=
	{InverseTrig -> False}

    In[4]:=
	intercept = True
    Out[4]=
	True

Here, the built-in rule fires.

    In[5]:=
	PowerExpand[Sqrt[a^2]]
    Out[5]=
	a

Here, our rule fires.

    In[6]:=
	PowerExpand[Sqrt[a^2], InverseTrig->True]

	in condition, intercept = True, InverseTrig = True
	in body

    Out[6]=
	a

The question arises, why didn't the "in condition..." print statement
show up during evaluation of In[5], and twice during In[6]?

We attempt to make our defined behavior the default:

    In[7]:=
	SetOptions[PowerExpand, InverseTrig->True]
    Out[7]=
	{InverseTrig -> True}

It doesn't work!

    In[8]:=
	PowerExpand[Sqrt[a^2]]
    Out[8]=
	a

Alright, now the worms start to squirm.  It turns out that there
are some system-defined DownValues for PowerExpand. Before we can
see them, we have to clear the ReadProtected attribute:

    In[9]:=
	ClearAttributes[PowerExpand, ReadProtected]

    In[10]:=
	First /@ DownValues[PowerExpand]
    Out[10]=
	{Literal[PowerExpand[System`Private`expr_]], 
	  Literal[PowerExpand[System`Private`expr_, {}]], 
	  Literal[PowerExpand[System`Private`expr_, 
	    {System`Private`a_}]], 
	  Literal[PowerExpand[System`Private`expr_, 
	    {System`Private`a_, System`Private`r__}]], 
	  Literal[PowerExpand[expr_, opts___Rule] /; 
	    With[{it = 
	       InverseTrig /. {opts} /. Options[PowerExpand]}\
	      , Print[in condition, intercept = , intercept\
		, , InverseTrig = , it]; intercept && it]], 
	  Literal[PowerExpand[System`Private`expr_, 
	    System`Private`a_]], 
	  Literal[PowerExpand[System`Private`args___]]}

As you can see, our rule for PowerExpand does NOT take precedence
over all of the built-in rules.  Thus, unless the InverseTrig->True
is manifested in the command, our rule will not fire!

To fix this problem, we can re-order the DownValues manually.
Here's the technique:

    In[11]:=
	{a,b,c,d}[[{3,1,2,4}]]
    Out[11]=
	{c, a, b, d}

Now we go for it:

    In[12]:=
	DownValues[PowerExpand] =
		DownValues[PowerExpand][[{5,1,2,3,4,6,7}]];

    In[13]:=
	First /@ DownValues[PowerExpand]
    Out[13]=
	{Literal[PowerExpand[expr_, opts___Rule] /; 
	    With[{it = 
	       InverseTrig /. {opts} /. Options[PowerExpand]}\
	      , Print[in condition, intercept = , intercept\
		, , InverseTrig = , it]; intercept && it]], 
	  Literal[PowerExpand[System`Private`expr_]], 
	  Literal[PowerExpand[System`Private`expr_, {}]], 
	  Literal[PowerExpand[System`Private`expr_, 
	    {System`Private`a_}]], 
	  Literal[PowerExpand[System`Private`expr_, 
	    {System`Private`a_, System`Private`r__}]], 
	  Literal[PowerExpand[System`Private`expr_, 
	    System`Private`a_]], 
	  Literal[PowerExpand[System`Private`args___]]}

Our rule is now at the top.  Note that the condition is evaluated
twice below; once successfully, and once unsuccessfully:

    In[14]:=
	PowerExpand[Sqrt[a^2]]

	in condition, intercept = True, InverseTrig = True
	in body
	in condition, intercept = False, InverseTrig = True

    Out[14]=
	a

This is the kind of stuff that rears its ugly head when you start
trying to override the behavior of built-in functions.  In general,
the numerical built-in functions don't have explicit DownValues (they are
implemented completely internal to the kernel), but quite a few of
the algebraic simplification commands do have external DownValues.
You have to assume the worst in all cases.

Also, NEVER do the following:

    Unprotect[foo];
    foo[my arguments] := my body
    (* Damn, I screwed up *)
    Clear[foo]

If you do, you wipe out the built-in rules and the function ceases to
do anything.  (Guess how I found this out? :-)).  If you find yourself
in a situation like this, BEFORE doing the Clear, you can either use
UnSet to remove your rule or surgically modify the DownValues.
AFTER doing the Clear, you either restart the kernel or you scrounge
through all of the files in Packages/StartUp looking for those
definitions that you wiped out!  (Not recommended!)

		Dave Wagner
		Principia Consulting
		(303) 786-8371
		princon at csn.net
		http://www.csn.net/princon


  • Prev by Date: Re: Extracting data points from Plot[]
  • Next by Date: Re: Rule Writing for x and 1/x
  • Previous by thread: Re: help w/ SphericalPlot3D
  • Next by thread: a plug for Block, Re: mg[758]