RationalQ – Testing for rationals in Mathematica

August 30th, 2009 | Categories: mathematica, Problem of the week | Tags:

Mathematica has a large set of functions that you can use to test the properties of numbers.  For example

IntegerQ[x]

returns True if x is an integer and False if it isn’t.  Of course you are not just restricted to asking if x is an integer or not.  For example, you can ask if it is an even number

EvenQ[x]

or an odd number

OddQ[x]

Perhaps you are wondering if x is prime

PrimeQ[x]

or even if it is an algebraic integer

AlgebraicIntegerQ[x]

The observant reader will notice that all of these functions end with a capital Q and a way of remembering this is to think that you are asking a question of the variable x.  So the question ‘Is x an integer‘ becomes, in Mathematica notation, IntegerQ[x].

I am currently working on a piece of code where I need to determine whether or not a particular number belongs to the set of rationals and I assumed that a suitable function would exist in Mathematica and that it would be called RationalQ[] so I was rather surprised to see that there is no such function in Mathematica 7.

So, I’ll just have to come up with my own.  A quick search resulted in the following function definition from Bob Hanlon

RationalQ[x_] := (Head[x] === Rational)

Which almost does what I need.  It handles the following correctly

RationalQ[1/2] (gives True)

RationalQ[Sqrt[2]] (Gives False)

but I needed a version of RationalQ that also returned True when passed an integer.  After all, the integers are just a subset of the rationals.  A moments thought resulted in

RationalQ[x_] := (Head[x] === Rational || IntegerQ[x]);

Which seems to work perfectly.  So, I offer the above function for anyone who is googling for a RationalQ function and I also ask the following questions to any Mathematica gurus who might be reading this

  • Is there anything wrong with the above definition?
  • Why isn’t such an obvious function not included in Mathematica as standard?
  1. Szabolcs
    August 30th, 2009 at 22:45
    Reply | Quote | #1

    IntegerQ is a function that tests whether the /data type/ of an expression is Integer, but not whether something is mathematically an integer. Therefore it is really redundant, Head[something] === Integer works the same. Use Simplify[something \[Element] Integers] to test if something is an integer mathematically (as the documentation suggests). Or, alternatively, FullSimplify.

    You did not tell us what you wanted to use this RationalQ function for, so I can’t really tell you if there is anything wrong with it. Here are a few examples where it might go wrong:

    For your particular application, do you consider the Mathematica expression 1.2 a rational number? Do you consider the expression Cos[\[Pi]/8]^2 – 1/(2 Sqrt[2]) a rational number? (Its value is 1/2, but it is not automatically simplified by Mathematica.)

    I hope this helps,
    Szabolcs

  2. kiwi
    August 30th, 2009 at 23:33
    Reply | Quote | #2

    Or you could use Element[x,Rationals]

  3. August 31st, 2009 at 03:19
    Reply | Quote | #3

    Hi Mike,

    There is also no object like RealQ or ComplexQ, this is because Mma has a set of predefined domains,
    Reals, Integers, Complexes, Algebraics, Primes, Rationals and Booleans
    You can ask if something (x) is in a domain using
    Element[x, domain]
    It works this way since these are the domains that you can tell Reduce (and other functions) to work in — also they can be passed to Assumptions – ie they work for variables as well as numbers.

    As for your code, it should work for any numerical object you want to test…
    but if you’ve your using the assumptions (say, set globally)
    $Assumptions = {Element[n,Integers]}
    then
    (n/2) \[Element] Rationals // Simplify
    returns
    True
    but would return False with your code.

    In fact that reminds me of a place where your code might troublesome:
    If[RationalQ[x/2], Print[“ok”], Print[“not ok”]] /. x -> 2
    would return
    “not ok”
    with your definition – since if RationalQ will always return False if it’s not an integer or rational.
    It is better to leave the undecided case unevaluated, so the following code would be more robust:
    RationalQ[x_Integer] := True
    RationalQ[x_Rational] := True
    and (remembering to Clear the previous definition of RationalQ)
    If[RationalQ[x/2], Print[“ok”], Print[“not ok”]] /. x -> 2
    returns
    “ok”
    If you want to force an answer you can always use
    RationalQ[x/2]===True
    in a conditional.

    sorry for the essay!

    Simon

  4. August 31st, 2009 at 03:20
    Reply | Quote | #4

    PS
    my indenting was removed by your blogging software — I probably should have used html tags…

  5. I. J. Kennedy
    August 31st, 2009 at 05:37
    Reply | Quote | #5

    Beware of this oddity:

    It’s possible to use the Rational constructor to form perverse rationals such as
    p = Rational[π,3]
    (That’s pi over 3).
    What exactly is that thing? Let’s take a look numerically:

    In[2]= N[p]
    Out[2]= 3.14159 /3

    That’s sort of numerical, put not quite all the way flattened for some reason.
    Applying the N hammer once more

    In[3]= N[%]
    Out[3]= 1.0472

    So this creature does have a normal numeric value, but yet p would be considered
    Rational, since its head is Rational.

  6. August 31st, 2009 at 09:03
    Reply | Quote | #6

    Mike,

    I’m using almost the same thing, although mine is 3 definitions:

    RationalQ[x_Rational] := True
    RationalQ[x_Integer] := True
    RationalQ[x_] := False

    I’m using the pattern matching directly, which is probably faster (not tested).

    Cheers,

    Sander

  7. Mike Croucher
    September 1st, 2009 at 10:22
    Reply | Quote | #7

    Thanks for all of the comments people. My function works just fine for my particular application but it obviously has issues that are addressed by your solutions.

    I was thinking…from a certain point of view, ALL floating point numbers belong to the rationals since they all have a finite number of decimal places. This is almost certainly not what people expect though.

    Thanks again for the discussion,
    Mike

  8. Szabolcs
    September 1st, 2009 at 19:03
    Reply | Quote | #8

    A comment on a previous comment:

    While it is possible to write things like Rational[Pi,3] or Complex[a,b], these symbols (Rational and Complex) were not intended to be used this way (i.e. for building expressions). Doing it anyway just invites trouble.

    The properly written expression Pi/3 or Divide[Pi,3] does not have head Rational (its FullForm is Times[Rational[1,3], Pi]).

  9. Simon
    September 2nd, 2009 at 17:10
    Reply | Quote | #9

    Another comment on the same previous comment:

    Hi I. J. Kennedy, I tried to reproduce your results using Mathematica 7.0 for Linux x86 (64-bit), but got something different,

    In[1]:= p=Rational[\[Pi],3]
    Out[1]= Rational[\[Pi],3]

    In[2]:= N[p]
    Out[2]= 3.14159/3.

    In[3]:= N[%]
    Out[3]= 3.14159/3.

    In[4]:= FixedPoint[N,p]
    Out[4]= 3.14159/3.

    obviously if you cut and paste the output, you get

    In[5]:= N[3.141592653589793`/3.`]
    Out[5]= 1.0472

  10. Simon
    September 2nd, 2009 at 17:12

    Also, here’s a quick and dirty timing test:

    In[1]:= {r,f}={1/3,.3};
    reps=10^6;

    In[3]:= $TimeUnit
    Out[3]= 1/100

    In[4]:= ClearAll[RationalQ];ClearSystemCache[]
    RationalQ[x_]:=(Head[x]===Rational||IntegerQ[x]);
    Timing[Do[RationalQ/@{r,f},{reps}]]
    Out[6]= {11.1807,Null}

    In[7]:= ClearAll[RationalQ];ClearSystemCache[]
    RationalQ[x_Integer]:=True
    RationalQ[x_Rational]:=True
    Timing[Do[RationalQ/@{r,f},{reps}]]
    Out[10]= {5.20433,Null}

    In[11]:= ClearAll[RationalQ];ClearSystemCache[]
    RationalQ[x_]:=Element[x,Rationals]
    Timing[Do[RationalQ/@{r,f},{reps}]]
    Out[13]= {6.02838,Null}