8

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

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
Nasser
  • 143,286
  • 11
  • 154
  • 359
  • Does only the sign of the highest-order term of h count for your decision? – Yves Klett Aug 22 '13 at 14:53
  • 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.... – Nasser Aug 22 '13 at 14:58

8 Answers8

6

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}
ArgentoSapiens
  • 7,780
  • 1
  • 32
  • 49
6

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}

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
5

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} *)

Yves Klett
  • 15,383
  • 5
  • 57
  • 124
  • For additional historical information, look here. – Yves Klett Aug 22 '13 at 14:23
  • 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 – Nasser Aug 22 '13 at 14:39
  • @Nasser Well, it was only designed for products... let´s see – Yves Klett Aug 22 '13 at 14:46
  • 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. – Nasser Aug 22 '13 at 14:52
5

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} *)
Michael E2
  • 235,386
  • 17
  • 334
  • 747
  • 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. – Nasser Aug 22 '13 at 15:17
  • @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. – Michael E2 Aug 22 '13 at 15:19
  • Sure. I know you knew that and were just checking on requirements. Will add it to the question to be more clear. thanks. – Nasser Aug 22 '13 at 15:21
3

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}

Carl Woll
  • 130,679
  • 6
  • 243
  • 355
3

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 *)
Simon Woods
  • 84,945
  • 8
  • 175
  • 324
3

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 *)
Alexei Boulbitch
  • 39,397
  • 2
  • 47
  • 96
2
  dummy = (1.0/1.0);
  Negative[Extract[dummy*(#), 1]] & /@ negativeTruetests
   (* {True, True, True} *)

  Negative[Extract[dummy*(#), 1]] & /@ negativeFalseTests
   {False, False, False}
Hubble07
  • 3,614
  • 13
  • 23
  • 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. – Nasser Aug 22 '13 at 14:31
  • Multiplying by that dummy seems to work.I know its ugly but it works right:). – Hubble07 Aug 22 '13 at 14:40
  • 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. – Nasser Aug 22 '13 at 14:44
  • Why cant you do like Negative[Extract[dummy*(Last@FDFormula[]), 1]] – Hubble07 Aug 22 '13 at 14:50
  • I tried you solution like this: Negative[Extract[dummy*negativeFalseTests, 1]] and I get this: Mathematica graphics – Nasser Aug 22 '13 at 15:04
  • @Nasser How about now.I have edited my answer – Hubble07 Aug 22 '13 at 15:08