8
$\begingroup$

I am using a Mathematica function which returns some error term in symbolic form. I needed a way to determine if this term starts with a minus sign or not. There will be only one term. This is to avoid having to worry about Mathematica auto arranging terms like x-h to become -h+x and hence it is not fair to ask for more than one term solution.

Here are some examples of the expressions, all in input form

negativeTruetests = {(-(71/12))*h^2*Derivative[4][f],
(-1)*h^2*Derivative[10][f],
(-(359/3))*h^2*Derivative[10][f], -2*h, -x*h^-2, -1/h*x }

negativeFalseTests = { h^2*Derivative[4][f] , h^2*Derivative[4][f],
33*h^2*Derivative[4][f], h^-2, 1/h}

I need a pattern to check if the expression starts with minus sign or not.

Mathematica graphics

$\endgroup$
2
  • $\begingroup$ Does only the sign of the highest-order term of h count for your decision? $\endgroup$
    – Yves Klett
    Aug 22, 2013 at 14:53
  • $\begingroup$ Only ONE term, and hence, the term, if it has a minus sign, it will have to be the first thing (looking at it, not in tree form). So, only ONE term. Hence -h-f will not show up. Only (- h) * derivative(something) or h*derivative or number*h*derivative etc.... $\endgroup$
    – Nasser
    Aug 22, 2013 at 14:58

8 Answers 8

6
$\begingroup$

I will use your test lists alone as reference. If they are incomplete you will need to update the question.

When looking for a pattern for a group of expressions it is helpful to look at their TreeForm:

TreeForm /@ {negativeTruetests (*sic*), negativeFalseTests} // Column

enter image description here

enter image description here

You see that your True expressions always have the head Times with one negative leaf, be it -1, -2 or a Rational that is negative. Your False expressions either have head Times or Power but in the case of Times they do not have a negative leaf. Therefore for these expressions you may use:

p = _. _?Negative;

MatchQ[#, p] & /@ negativeTruetests
MatchQ[#, p] & /@ negativeFalseTests

{True, True, True, True, True, True}

{False, False, False, False, False}

Because of the Optional and OneIdentity(1) this pattern will also handle a negative singlet:

MatchQ[#, p] & /@ {-Pi, 7/22}

{True, False}


Format-level pattern matching

Since it was revealed that this question relates to formatting it may be more appropriate to perform the test in that domain.

I will use a recursive pattern as I did for How to match expressions with a repeating pattern after converting to boxes with ToBoxes:

test = MatchQ[#, RowBox[{"-" | _?#0, __}]] & @ ToBoxes @ # &;

test /@ negativeTruetests
test /@ negativeFalseTests

{True, True, True, True, True, True}

{False, False, False, False, False}

Another approach is to convert to a StandardForm string and use StringMatchQ, which is essentially the same as the test above because StandardForm uses encoded Boxes:

test2 = StringMatchQ[ToString[#, StandardForm], ("\!" | "\(") ... ~~ "-" ~~ __] &;

test2 /@ negativeTruetests
test2 /@ negativeFalseTests

{True, True, True, True, True, True}

{False, False, False, False, False}

$\endgroup$
6
$\begingroup$

You can do it the way a human does it: Look for the "-" in the string.

#==1&/@StringCount[ToString/@InputForm/@negativeTruetests,Alternatives["-",Repeated["("]~~"-"]]

{True,True,True}

#==1&/@StringCount[ToString/@InputForm/@negativeFalseTests,Alternatives["-",Repeated["("]~~"-"]]

{False,False,False}
$\endgroup$
5
$\begingroup$

There are probably many creative ways to break this one horribly, but for the test cases it works:

(# /. Thread[Variables[#] -> 1]) < 0 & /@ negativeTruetests

(* {True, True, True, True, True, True} *)

(# /. Thread[Variables[#] -> 1]) < 0 & /@ negativeFalseTests

(* {False, False, False, False, False} *)

$\endgroup$
4
  • $\begingroup$ For additional historical information, look here. $\endgroup$
    – Yves Klett
    Aug 22, 2013 at 14:23
  • $\begingroup$ You have a bug: expr = h^2*Derivative[4][f]- 2 h then (expr /. Thread[Variables[expr] -> 1]) < 0 it gives true. But it should be False ! I knew this was not as easy as it looked :) will add this to the tests $\endgroup$
    – Nasser
    Aug 22, 2013 at 14:39
  • $\begingroup$ @Nasser Well, it was only designed for products... let´s see $\endgroup$
    – Yves Klett
    Aug 22, 2013 at 14:46
  • $\begingroup$ I think it is ok. Adding terms is not fair. Since Mathematica will rearrange terms on its own. So x-h might end up as -h+x. So let just leave it at one term for now. I did not mean for this to become too hard. $\endgroup$
    – Nasser
    Aug 22, 2013 at 14:52
5
$\begingroup$

All of your examples are of the form Times[...], which case you can do this (on the original examples):

MemberQ[#, _?Negative] & /@ Flatten[{negativeFalseTests, negativeTruetests}]
(* {False, False, False, True, True, True} *)

or this

expr /. {Times[c_?NumericQ, ___] :> c < 0, _Times :> False}

but not this

MemberQ[#, _?Negative] & /@ {h^-2}
(* {True} *)

Edit

Here's a modification of the second method that works on powers, now with the updated examples:

# /. {Times[c_?NumericQ, ___] :> c < 0, _ :> False} & /@ 
   Flatten[{negativeFalseTests, negativeTruetests}]
(* {False, False, False, False, False, True, True, True, True, True, True} *)
$\endgroup$
3
  • $\begingroup$ Yes, the last case, h^-2 should NOT be negative. THere is only ONE term, all symbolic, but can start with a minus sign or not. Hence $h^{-2}$ should not be valid for saying it starts with minus sign. For example, MemberQ[#, _?Negative] & /@ {1/h} return TRUE, which is not right. $\endgroup$
    – Nasser
    Aug 22, 2013 at 15:17
  • $\begingroup$ @Nasser I know that. I was asking whether that use-case is to be handled or not. I take it might arise and you would like a solution that includes it. $\endgroup$
    – Michael E2
    Aug 22, 2013 at 15:19
  • $\begingroup$ Sure. I know you knew that and were just checking on requirements. Will add it to the question to be more clear. thanks. $\endgroup$
    – Nasser
    Aug 22, 2013 at 15:21
3
$\begingroup$

This function

 checkMinus[expr_] := 
 Select[expr /. Head[expr] -> List, # < 0 &] // Length

applied to your terms returns 1, if there is a minus in the expression with the structure you indicated, and 0 in the opposite case.

checkMinus[-((h^12 m'[0])/24)]

(* 1  *)

checkMinus[(h^4 m'[0])/12]

(* 0 *)
$\endgroup$
3
$\begingroup$

How about looking at the displayed boxes? Specifically, look for the first RowBox containing a string as its first element (as opposed to another box), and check to see if that string is a minus sign:

SetAttributes[isMinus, {HoldFirst}];
isMinus[expr_] := Cases[MakeBoxes[expr], RowBox[{s_String, ___}] :> s, {0, -1}, 1] == {"-"}

isMinus /@ negativeTruetests
(* {True, True, True} *)

isMinus /@ negativeFalseTests
(* {False, False, False} *)

isMinus doesn't evaluate its argument, so for example:

isMinus[h^2*Derivative[4][f] - 2 h]
(* False *)

The first term has no minus sign so the result is false. However, if Mathematica is allowed to evalute the argument it will rearrange the expression to put -2h at the beginning, so:

isMinus[Evaluate[h^2*Derivative[4][f] - 2 h]]
(* True *)
$\endgroup$
0
3
$\begingroup$

You can make use of the undocumented, internal function Internal`SyntacticNegativeQ for this purpose (see this answer):

Internal`SyntacticNegativeQ /@ negativeTruetests

{True, True, True, True, True, True}

Internal`SyntacticNegativeQ /@ negativeFalseTests

{False, False, False, False, False}

$\endgroup$
2
$\begingroup$
  dummy = (1.0/1.0);
  Negative[Extract[dummy*(#), 1]] & /@ negativeTruetests
   (* {True, True, True} *)

  Negative[Extract[dummy*(#), 1]] & /@ negativeFalseTests
   {False, False, False}
$\endgroup$
6
  • $\begingroup$ THat does not work? Negative[Extract[ h^2*Derivative[4][f], 1]] gives Negative[h^2] It has to work even if there is no number at the start of the expression. I'll add that as an example also. $\endgroup$
    – Nasser
    Aug 22, 2013 at 14:31
  • $\begingroup$ Multiplying by that dummy seems to work.I know its ugly but it works right:). $\endgroup$
    – Hubble07
    Aug 22, 2013 at 14:40
  • $\begingroup$ Nope. Expression returned does not have a dummy. Have to use the expressions as shown. Can't modify them. These are returned from a function. $\endgroup$
    – Nasser
    Aug 22, 2013 at 14:44
  • $\begingroup$ Why cant you do like Negative[Extract[dummy*(Last@FDFormula[]), 1]] $\endgroup$
    – Hubble07
    Aug 22, 2013 at 14:50
  • $\begingroup$ I tried you solution like this: Negative[Extract[dummy*negativeFalseTests, 1]] and I get this: !Mathematica graphics $\endgroup$
    – Nasser
    Aug 22, 2013 at 15:04

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.