How do I make $0 \times (\mathsf{anything})=0$?

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

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.


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
   }
]