8
$\begingroup$

For a matrix like

F = {{β , x β}, {-2 β, -3 β}};
F // MatrixForm

I would like to set to 0 all terms which contain a minus, in this case the bottom row, getting

Fp = {{β , x β}, {0, 0}}

I have received 4 answers to this question which work fine, but seem to stop working if the matrix contains fractions, as in the example:

M = {{s \[Beta]/(c + a), 0}, {0, s \[Beta]/(b + a)}};
M /. _?Negative -> 0
M /. _?Internal`SyntacticNegativeQ -> 0
M /.  x_ /; x < 0 -> 0
M /. _. _?Negative -> 0

Is there a way to make this work with denominators as well?

$\endgroup$
1

3 Answers 3

8
$\begingroup$

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.

$\endgroup$
4
  • $\begingroup$ Thanks! I want to use this in a script, so I must choose between number 2 and number 4. Which would you recommend? $\endgroup$
    – florin
    Jul 9 at 9:39
  • 1
    $\begingroup$ @florin Good question. Perhaps I should have thought of it earlier. Consider which behavior you want if entries have symbolic constants. For example, Replace[{{(1 - Pi) x}}, _. _?Negative -> 0, {2}] or Replace[{{(1 - Pi) x}}, _?Internal`SyntacticNegativeQ -> 0, {2}]? Maybe such a case doesn't arise for you, but there is a difference. And to make things worse, perhaps consider the case of two negative factors: Replace[{{(1 - Pi) (1 - E) x}}, _. _?Negative -> 0, {2}]. Maybe you need something like Replace[{{(1 - Pi) (1 - E) x}}, expr_ /; MatchQ[N[expr], _. _?Negative] -> 0, {2}]. $\endgroup$
    – Michael E2
    Jul 9 at 14:05
  • $\begingroup$ Thanks again. It will take me a while to digest all this. But what I needed is just to eliminate "outflows", i.e. terms which are typeset with a minus, because in my field (epidemiology, etc) we assume always that the symbolic constants are nonnegative. As to your last examples, I guess I can avoid them by applying first something like Apart. $\endgroup$
    – florin
    Jul 9 at 17:42
  • $\begingroup$ I am using this command in a paper I will publish. I feel a bit funny, that I am sort of a retailer. I am selling powerful Mathematica capabilities known only to users like @Michael E2 , @kglr , @Nasser , in an applied dynamical systems subfield, where people got used for 50 years to do everything by hand. So I added a quasi-reference "Note that the Replace" command uses the powerful Mathematica capability of applying a Rule" to an object, and that it was furnished to us by the user @Michael E2 in mathematica.stackexchange.com/questions/286500..." Hope it's OK... $\endgroup$
    – florin
    Jul 10 at 6:12
8
$\begingroup$
F /. _?Internal`SyntacticNegativeQ -> 0
 {{β, x β}, {0, 0}}

Also

F /. _?Negative -> 0
 {{β, x β}, {0, 0}}
F /.  x_ /; x < 0 -> 0
 {{β, x β}, {0, 0}}
Simplify[Ramp @ F, Variables[F] ∈ PositiveReals]
 {{β, x β}, {0, 0}}
$\endgroup$
3
  • 1
    $\begingroup$ Why does F /. x_ /; x < 0 -> 0 work? For example if I do ClearAll[a]; -a /. x_ /; x < 0 -> 0 it gives 0. But how does it know -a is negative? Since a has no value. TrueQ[-a < 0] gives False. Also If[-a<0,"negative","not negative","do not know"] gives "do not know" as expected, I am now confused. $\endgroup$
    – Nasser
    Jun 14 at 5:53
  • 2
    $\begingroup$ @Nasser, good question. Pattern matching works on the FullForm (which is, Times[-1,a]), and the replacement rule makes -1 (not a) zero. Try also -a /. x_ /; x < 0 -> FOO[x] to see this more clearly. $\endgroup$
    – kglr
    Jun 14 at 6:04
  • 2
    $\begingroup$ Ah, so the x in x_ /; x < 0 is really the FIRST ARGUMENT of the Times[-1, a]. Here is another example. expr = Times[-1, a]; expr /. x_ /; x < 0 -> "0" gives "0" a This is because First@Times[-1, a] gives -1. I was thinking it is looking at the whole thing (which is -a) Not the first argument of the expression which is -1. I do not think I'll ever really understand Mathematica's pattern matching well :) I wonder now why then this was not given as answer when I asked about this in the link I have in my answer below. It seems much simpler solution then. $\endgroup$
    – Nasser
    Jun 14 at 6:12
4
$\begingroup$

Another option is using Mr.Wizard trick in Given a symbolic expression how to find if starts with a minus or not?

F={{β,x β},{-2 β,-3 β}};
F/. _. _?Negative->0

Mathematica graphics

$\endgroup$
1
  • $\begingroup$ Thanks a lot; unfortunately, this, as well as the solutions of @kglr stop working when the matrix has denominators, which are destroyed as well. See new example $\endgroup$
    – florin
    Jul 8 at 15:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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