Why ReplaceAll[]
fails: Since the proposed rules replace any negative by 0
, when applied at all levels, we get things like Power[denominator, -1]
transformed to Power[denominator, 0]
, which is 1
. And so forth.
The fix: Use Replace[]
instead of ReplaceAll[]
on the individual matrix entries:
M = {{s \[Beta]/(c + a), 0}, {0, s \[Beta]/(b + a)}};
F = {{\[Beta], x \[Beta]}, {-2 \[Beta], -3 \[Beta]}};
M == Replace[M, _?Negative -> 0, {2}] (* Succeeds *)
M == Replace[M, _?Internal`SyntacticNegativeQ -> 0, {2}](* Succeeds *)
M == Replace[M, x_ /; x < 0 -> 0, {2}] (* Succeeds *)
M == Replace[M, _. _?Negative -> 0, {2}] (* Succeeds *)
Replace[F, _?Negative -> 0, {2}] (* FAILS *)
Replace[F, _?Internal`SyntacticNegativeQ -> 0, {2}] (* Succeeds *)
Replace[F, x_ /; x < 0 -> 0, {2}] (* FAILS *)
Replace[F, _. _?Negative -> 0, {2}] (* Succeeds *)
Restricting the level at which the rules are applied causes some of them to fail. I included them to show that Replace[mat, rule, {2}]
does not work for every rule
.
We see that two patterns handle both examples correctly:
_?Internal`SyntacticNegativeQ
_. _?Negative
(The OP's M
is an odd example in that no change is supposed to occur. Both good rules and bad rules might succeed at doing nothing for both good and bad reasons.)
Addendum 2023.07.09
[A symbolic edge-case and numerical edge-cases are discussed. If things are as simple as one might imagine from the OP's examples — such as all coefficients are integers by hypothesis — then nothing in this addendum, whether the symbolic edge case of two negative factors nor the numerical issues, applies. Under such a hypothesis, one of the simpler patterns above may be used. If you have more complicated expressions, then the issues below may interest you.]
Thanks to a good question by @florin, I have an alternative pattern to propose, which would be useful if the criterion were a negative coefficient and not whether the entry is typeset with a leading negative sign. (The first condition is mathematical and the second has to do notational conventions. So the first seems to have greater usefulness in my mind, since we're replacing entries by 0
, which seems a mathematical operation and not a typesetting/output-formatting one. Whether or not my inkling is correct in the OP's case, its potential usefulness to others makes the alternative worth including in an answer.) Three options: the first is faster, the second is more rigorous, and the third is slowest, rigorous, and more robust:
expr_ /; MatchQ[N[expr], _. _?Negative]
expr_ /; MatchQ[N[expr, 1.5], _. _?Negative]
expr_ /; MatchQ[N[expr, 1.5], _. _?(Simplify@*Negative)]
Examples (success = replacement iff numeric coefficient is negative):
Replace[{{(1 - Pi) (1 - E) x}}, (* succeeds *)
expr_ /; MatchQ[N[expr], _. _?Negative] -> 0, {2}]
Replace[{{(1 - Pi) (1 - E) x}}, (* FAILS *)
_. _?Negative -> 0, {2}]
(*
{{(1 - E) (1 - \[Pi]) x}}
{{0}}
*)
Replace[{{(1 - Pi) x}}, (* succeeds *)
expr_ /; MatchQ[N[expr], _. _?Negative] -> 0, {2}]
Replace[{{(1 - Pi) x}}, (* succeeds *)
_. _?Negative -> 0, {2}]
(*
{{0}}
{{0}}
*)
Numerical warnings:
$1.$ N[numeric]
is not guaranteed to give a number with the correct sign. For example:
N[(1 + Sqrt[2])^4 - (17 + 12 Sqrt[2]) + 2^-52]
(* -7.10543*10^-15 *)
N[(1 + Sqrt[2])^4 - (17 + 12 Sqrt[2]) + 2^-52, 1.5]
(* 2.2*10^-16 *)
Since (1 + Sqrt[2])^4
equals 17 + 12 Sqrt[2]
, the result should be positive.
$2.$ Generally N[numeric, 1.5]
will give the correct sign or fail with an error message. My general rule of thumb is that all numerical methods have limitations, but I do not know off hand an example for which N[numeric, 1.5]
gives the wrong answer.
N[(1 + Sqrt[2])^4 - (17 + 12 Sqrt[2]) + 2^-1052, 1.5]
N::meprec: Internal precision limit $MaxExtraPrecision = 50.` reached while evaluating....
(* 0.*10^-50 = 0``49.22856878310697 *)
Block[{$MaxExtraPrecision = 320},
N[(1 + Sqrt[2])^4 - (17 + 12 Sqrt[2]) + 2^-1052, 1.5]
]
(* 2.1*10^-317 *)
Note that both Positive[0``49.22856878310697]
and Negative[0``49.22856878310697]
do not evaluate, since 0``49.22856878310697
is of indeterminate sign. They could evaluate to Indeterminate
, maybe, but they do not, I suppose because Indeterminate
represents a numerical quantity not an undetermined boolean value.
$3.$ Likewise Positive[]
and Negative[]
remain unevaluated on (1 + Sqrt[2])^4 - (17 + 12 Sqrt[2]) + 2^-1052
with a similar N::meprec
message. However, applying Simplify
to the failed Positive[]
or Negative[]
call results in a valid True
or False
.
This brings us to the third alternative pattern mentioned above, but not shown in the examples:
Simplify[Negative[(1 + Sqrt[2])^4 - (17 + 12 Sqrt[2]) + 2^-1052]]
N::meprec: Internal precision limit $MaxExtraPrecision = 50.` reached while evaluating....
(* False *)
Adding Simplify
or FullSimplify
will take care of some cases which Negative[expr]
leaves undecided.