5
$\begingroup$

I have a complicated formula which is a sum of several terms. Any given term will be a product of factors: $a_1(x)\times a_2(x)\times a_3(x)\times \ldots$, in which for simplicity I have shown that each factor is a function of only one variable $x$. I want to make the convention that if, for some $x=x_0$, even one factor $a_i(x_0)=0$, the whole term becomes zero, irrespective of the value of the other factors in the term (some of which may evaluate to infinity at $x=x_0$).

How do I do that in Mathematica in a general way? For example, one of the terms in my long formula looks like this:

(t + s - 1) (t + s)LegendreQ[s-1, t-1, x]LegendreP[s-1, t-1, n]

At t=1 s=0, (t + s - 1)=0 but LegendreQ evaluates to ComplexInfinity, and so the whole expression evaluates to Indeterminate, but I want the expression to evaluate to 0. Of course above is only a sample, and if possible I would like something general enough that can deal with arbitrary expressions.

$\endgroup$
  • $\begingroup$ @Bill Number of factors in a term is not fixed in my case, so even if your suggestion worked I couldn't use it. $\endgroup$ – Deep May 17 '18 at 8:39
  • $\begingroup$ This breaks math. You really want $x \frac{1}{x}=0$ for $x=0$? $\endgroup$ – Vsevolod A. May 18 '18 at 20:52
  • 1
    $\begingroup$ @VsevolodA. Good point. I worried about that myself. The correct procedure I guess is to evaluate each term in the limit $x\to x_0$. $\endgroup$ – Deep May 19 '18 at 5:11
4
$\begingroup$

Updated solution

@Michael E2 pointed out some issues with the previous solution, so here's another take:

expr = (t + s - 1) (t + s) LegendreQ[s - 1, t - 1, x] LegendreP[s - 1, t - 1, n]

myTimes[___,0, ___] := 0
myTimes[args___] := Times[args]

expr /. {Times -> myTimes, t -> 1, s -> 0}
(* 0 *)

This works by replacing Times with myTimes before inserting the numerical values. myTimes has two properties:

  • myTimes with a 0 factor is 0
  • myTimes[args___] of anything else is the same thing as Times[args]

Old solution

The following should do what you want:

expr = (t + s - 1) (t + s) LegendreQ[s - 1, t - 1, x] LegendreP[s - 1, t - 1, n]

Activate[
 Inactivate[Evaluate@expr, Times] /.
   {t -> 1, s -> 0} /.
  Inactive[Times][___, 0, ___] :> 0
 ]

(* 0 *)

It works like this:

  • Apply Inactivate to all products
  • Insert the values for s and t
  • Replace any products containing a 0 factor with 0
  • Activatethe whole expression again
$\endgroup$
  • $\begingroup$ What do you mean? It correctly returns ComplexInfinity, or am I missing something? $\endgroup$ – Lukas Lang May 18 '18 at 11:53
  • $\begingroup$ Sorry, copied the wrong code. This is one that gives Indeterminate: expr = (t/(s + 1) - 1) LegendreQ[s - 1, t - 1, x] $\endgroup$ – Michael E2 May 18 '18 at 12:03
  • $\begingroup$ @MichaelE2 That's strange - I can't reproduce that. For me, it returns 0 as it should $\endgroup$ – Lukas Lang May 18 '18 at 13:17
  • $\begingroup$ Here's what I did: i.stack.imgur.com/qLOvM.png -- Does it look I'm doing the right thing? The reason (I think) it fails is that Inactivate[Evaluate@expr, Times] /. {t -> 1, s -> 0} does not contain an explicit multiplication by 0, since the factor (t/(s + 1) - 1) does not evaluate to 0 until after Activate. $\endgroup$ – Michael E2 May 18 '18 at 13:30
  • $\begingroup$ @MichaelE2 good point, not sure why it seems to work for me... I've added an alternative solution that shouldn't have any of these shortcomings $\endgroup$ – Lukas Lang May 18 '18 at 19:12
3
$\begingroup$

Similar in spirit to Mathe's answer:

With[{t = 1, s = 0}, 
 Block[{LegendreQ}, (t + s - 1) (t + s) LegendreQ[s - 1, t - 1, x] 
    LegendreP[s - 1, t - 1, n]]]
0

This is because MMA, in principle, already replaces anything times exact 0 to 0. When LegendreQ is blocked, it doesn't have a chance to evaluate to ComplexInfinity so MMA can then apply its usual rules to products with 0.

By the time the Block is released, there is no LegendreQ remaining in the expression.

$\endgroup$
2
$\begingroup$

Here's a function you can wrap around an expression to inspect if there are any multiplications by 0 inside (and set those multiplications to zero if necessary):

SetAttributes[zeroCheck, HoldFirst];
zeroCheck[expr_] := ReplaceAll[
  Unevaluated[expr],
  t_Times :> If[
    AnyTrue[Unevaluated[t], TrueQ[# == 0] &],
    0,
    t
  ]
]

We can use it like this to deal with your problem:

In[12]:= Module[{
  t = 1,
  s = 0
},
  zeroCheck[(t + s - 1) (t + s) LegendreQ[s - 1, t - 1, x] LegendreP[s - 1, t - 1, n]]
]

Out[12]= 0

Note, however, that zeroCheck is HoldFirst, so the following will not work:

expr = (t + s - 1) (t + s) LegendreQ[s - 1, t - 1, x] LegendreP[s - 1, t - 1, n];
Module[{t = 1, s = 0}, zeroCheck[expr]]

Instead, you'd have to do something like this:

Clear[expr];
expr[t_, s_] := (t + s - 1) (t + s) LegendreQ[s - 1, t - 1, x] LegendreP[s - 1, t - 1, n];
With[{evaluated = expr[t, s]},
 Block[{t = 1, s = 0},
   zeroCheck[evaluated]
 ]
]

0

edit

I noticed that my definition of zeroCheck fails in certain situations (particularly, if fails to propagate zeroes inside of nested Times upwards). This new version should avoid this issue:

SetAttributes[zeroCheck2, HoldFirst];
zeroCheck2[expr_] :=
 ReleaseHold[
   Hold[expr] //. {
     t_Times /; Quiet[AnyTrue[Unevaluated[t], EqualTo[0]]] :> 0
   }
 ]

Compare, e.g., zeroCheck[\[Infinity]*(0*\[Infinity])] and zeroCheck2[\[Infinity]*(0*\[Infinity])].

edit 2 As pointed out by Michael E2, zeroCheck2 can produce side effects when testing for equality to zero. We can avert this with the following change:

SetAttributes[zeroCheck3, HoldFirst];
zeroCheck3[expr_] := ReleaseHold[
  Hold[expr] //. {
   t_Times /;
      Quiet[
       AnyTrue[
        Unevaluated[t], 
        Function[x, Unevaluated[x] == 0, HoldFirst]
       ]
      ] :> 0
   }
]
$\endgroup$
  • $\begingroup$ I don't know how bullet-proof you want this to be, but zeroCheck2[{0}*(1/0)] returns {Indeterminate} for me. So does foo = -2; zeroCheck2[(++foo)*(1/0)]; and ++ is evaluated twice (because EqualTo[0] evaluates each factor until one is True. So it's not safe on code with side-effects. +1 anyway. $\endgroup$ – Michael E2 May 18 '18 at 11:28
  • $\begingroup$ Good finds to break my code =). Personally I'm not going to worry about those cases too much because they seem quite far removed from what the OP needs. I'll briefly address the side-effects. $\endgroup$ – Sjoerd Smit May 18 '18 at 11:33
  • $\begingroup$ By the way, if you want this to work for every conceivable situation (like multiplication of Lists etc.), I don't think there's any other way than messing around with the definition of Times itself (and any related symbols like Infinity). That's probably not a good road walk. $\endgroup$ – Sjoerd Smit May 18 '18 at 11:44
  • $\begingroup$ Yep, I think it's probably impossible, in fact, because of internal rules for Times that cannot be overridden. $\endgroup$ – Michael E2 May 18 '18 at 11:46
2
$\begingroup$

At first, I thought it would be impossible, except for the simple kind of example of the OP's. Inactivate[expr, Times] only replaces the instances of Times literally appearing in expr, so its application is limited to cases in which the unevaluated code for expr contains all the Times, or at least the ones that would lead to Indeterminate, that would occur when expr is evaluated. Also, if expr already contains Inactive[Times], then this would probably be activated as a side effect, although it could be dealt with. Overriding Times is (1) difficult because there are internal rules that are applied automatically before the normal evaluation sequence, apparently including one for the case at hand, and (2) possibly dangerous to workings of system functions (see below). Nonetheless, I present a function zerofy that overrides Times that seems to work for symbolic calculation. The only way (I think) you can do it is by replacing the head Times when the head is evaluated, so that the internal rules are never invoked. This can be done with Block. I haven't tried this with numerical functions like Plot, FindRoot, etc. I doubt it would work, or even could be made to work.

Notes on the code: The indirection Hold[times]... /. Hold[times] -> times prevents infinite recursion (from the second rule for times[]). One could add rules for 0. and 0.``acc as desired.

ClearAll[zerofy];
SetAttributes[zerofy, HoldFirst];
zerofy[expr_] := Module[{times},
  SetAttributes[times,   (* some attributes of Times *)
   {Flat, Listable, OneIdentity, Orderless}]; 
  times[0, ___] := 0;    (* Orderless => matches a 0 anywhere *)
  times[args___] := Times[args];
  Block[{Times = Hold[times]}, expr] /. Hold[times] -> times
  ]

expr = (t + s - 1) (t + s) LegendreQ[s - 1, t - 1, x] LegendreP[s - 1, t - 1, n];
zerofy[expr /. {t -> 1, s -> 0}]
(*  0  *)

expr = (t/(s + 1) - 1) LegendreQ[s - 1, t - 1, x];
zerofy[expr /. {t -> 1, s -> 0}]
(*  0  *)

zerofy[{0, 1}/0]

TimeConstrained::timc: Number of seconds 1.` is not a positive machine-sized number or Infinity.

Power::infy: Infinite expression 1/0 encountered.

(*  {0, ComplexInfinity}  *)

Despite the messages, the answer is what was desired. The bizarre TimeConstrained error is one of those things that happens when you mess with core functions like Times, I guess:

Block[{Times = "h"}, Power[0, -1]]

TimeConstrained::timc: Number of seconds 1.` is not a positive machine-sized number or Infinity.

Power::infy: Infinite expression 1/0 encountered.

(*  ComplexInfinity  *)

The TimeConstrained error could be avoided by using Inactivate[expr, Times] and replacing Inactive[Times] by times; however, if expr already contains Inactive[Times], then these would be activated as a side effect.

$\endgroup$
  • $\begingroup$ I was toying in my head with a method like this and couldn't figure out how to make it work. Very nice! $\endgroup$ – Sjoerd Smit May 18 '18 at 19:25
1
$\begingroup$

Here's another Inactivate approach:

inactivateReplacement[expr_, rules_] := Activate @ ReplaceAll[
    Inactivate[expr, Except[Times|Plus|Power]],
    rules
]

For your example:

inactivateReplacement[
    (t+s-1) (t+s) LegendreQ[s-1, t-1, x] LegendreP[s-1, t-1, n],
    {t->1, s->0}
]

0

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.