Future of support for rational numbers

SWI currently provides partial support for rational numbers. From the reference manual (4.27.2.1 Arithmetic types):

“In the long term, it is likely that rational numbers will become atomic as well as a subtype of number. User code that creates or inspects the rdiv(M,N) terms will not be portable to future versions.”

I’m prepared to submit a feature request on improvements to rational number support but would like to make sure I understand what this “long term” view entails. Perhaps there are already ideas in play, but to me it means:

  1. Rationals are a subtype of numbers (as stated above) and not compound terms. Integers are a subtype of rationals as currently.

  2. This carries over to the “Standard Order of Terms” (4.7):

“Numbers are compared by value. Mixed integer/float are compared as floats. If the comparison is equal, the float is considered the smaller value.”

substituting “rational” for “integer”. (The canonical form of rational numbers include integers so it’s not possible to have an integer value which can be distinguished from an equivalent rational number.)

This means the standard sort predicates will work as expected on lists of mixed numbers (integers, rationals, and floats).

  1. For the most part, the current arithmetic predicates provide adequate support for rationals. However the division (/ operator) of two rational numbers (which include integers) should produce a rational number, not a float. This is not currently the case, i.e., X is 1/2 unifies X with 0.5, not 1 rdiv 2. (However X is 1 rdiv 2 / 2 does unify X with 1 rdiv 4.)

  2. Built-in predicates for “Analysing and Constructing Terms” (4.21) change to so that rationals work the same as other number types. Predicates that expect compound terms will no longer work with rationals.

  3. Native syntax. In general, programming language support for rational literals is mixed. Those that have it (Common Lisp, Python, Ruby, OWL, Guile, Wolfram, Julia) usually use the p/q syntax where p and q are integers and q!=0. However, this conflicts with the Prolog operator / used to construct compound terms. (Julia uses // which has a similar issue.) Ruby rational literals could work. They have a ‘r’ suffix, e.g., 3/2r, which is currently illegal Prolog syntax. Using a letter in a number format is a little reminiscent of the ‘e’ optionally used in floating point literals. (So maybe 3/2R should also be acceptable on input.)

Given the above, I think the rdiv operator is no longer required and could be deprecated.

Comments?.

I think all of that makes sense. Ruby style 3/2R would be acceptable syntax to me. 3/2r is a little dodgy, although 0b1101 is also valid (ISO) Prolog syntax while it is ambiguous if b is defined as an infix operator.

I don’t know how much will break if X is 1/2 no longer produces a float. The non-standard behavior of SWI-Prolog where X is 4/2 produces the integer 2 rather than the float 2.0 doesn’t seem to have much impact together with the redefined standard order that maintains numerical ordering (although numerical duplicates are not removed by sort/2):

?- sort([2,2.0], L).
L = [2.0, 2].

Now someone with enough time to implement it :slight_smile: It isn’t particularly hard and mainly implies adding a new indirect type similar to GMP integers, floats and strings, extend read and write and a few changes to pl-arith.c, though these are probably minimal as MPQ numbers are already used for intermediate results.

A still pending issue is that GMP is a great library, but licensed according to the LGPL, effectively turning SWI-Prolog to be LGPL when linked with GMP (the default). There are some initiatives to come up with a proper bignum library with a liberal license but the last time I looked at them this all looked pretty immature and/or poorly supported.

I think all of that makes sense. Ruby style 3/2R would be acceptable syntax to me. 3/2r is a little dodgy, although 0b1101 is also valid (ISO) Prolog syntax while it is ambiguous if b is defined as an infix operator.

As is 1e1 if e is defined as an infix operator. So I think there’s a valid precedent to permit r although it’s not a huge deal. And I’m not wedded to the Ruby syntax although it’s prior art and it works.

I don’t know how much will break if X is 1/2 no longer produces a float. The non-standard behavior of SWI-Prolog where X is 4/2 produces the integer 2 rather than the float 2.0 doesn’t seem to have much impact together with the redefined standard order that maintains numerical ordering (although numerical duplicates are not removed by sort/2):

?- sort([2,2.0], L).
L = [2.0, 2].

I did wonder about that, but I think SWIP does the right thing. And I think dividing two rationals should yield a rational result.

For term comparison purposes, 2\==2.0 (so really not a duplicate) and 11 rdiv 10 \== 1.1. In fact, 11 rdiv 10 isn’t even numerically equal to 1.1 due to the internal floating point conversion from decimal to binary.

Now someone with enough time to implement it :slight_smile: It isn’t particularly hard and mainly implies adding a new indirect type similar to GMP integers, floats and strings, extend read and write and a few changes to pl-arith.c , though these are probably minimal as MPQ numbers are already used for intermediate results.

I wish I could contribute. I have the time but, unfortunately, not the expertise in maintaining large C programs. Even if I could implement the changes, I’m not sure how I’d test them. But I’m open to suggestions. I can submit a formal feature request based on this discussion so that at least it would be tracked.I do appreciate this is a new feature subject to resource limitations. I can certainly workaround the current state of play but there’s a few warts (mainly because they’re not numbers).

A still pending issue is that GMP is a great library, but licensed according to the LGPL, effectively turning SWI-Prolog to be LGPL when linked with GMP (the default). There are some initiatives to come up with a proper bignum library with a liberal license but the last time I looked at them this all looked pretty immature and/or poorly supported.

Don’t know the full implications of this, but that’s the status quo. Without GMP (or equivalent) there are no rationals.

1 Like

As you probably know, there is term equivalence (==) and numerical equivalence (=:=), where the latter implies synchronizing the types and comparing according to the logic for that type. Not much changes to that when adding rational numbers.

Not sure how much more we need than this. It just waits for someone with either the money to pay someone to do this or someone sufficiently motivated with the right expertise to do the job. It would certainly be a step forward, but I’m afraid there are too many other plans on my agenda. I won’t say these are more important, but they are more important to the people that depend on SWI-Prolog and pay for it.

I see no errors or even strange things.

I guess JanB is just not happy with the type coercion. This is obviously not a “bug” by any definition of the word, it is a design choice.

PS: One of the small annoyances when switching between languages on a regular basis is that the meaning of arithmetic operators subtly differs. I find myself often enough having to dig in the documentation and doing small experiments to figure out what / does for a particular combination of operands, for example. This is just a tangential remark, I am not discussing the current or future implementation of rational numbers in SWI-Prolog.

He is not clear about that. The rules are a bit dubious because as long as rationals are not proper numbers we cannot support them easily in normal / division and needed the rdiv construct. Other than that, basically arithmetic that is defined on rationals and gets rational inputs create rational output. If an operand is a float, rationals are coerced to float and computation continues in the float world. In other words, if the result of the computation is rational, it is 100% precise. If it is a float the usual float oddities apply.

To make / behave as it should we must do the whole stuff as proposed by @ridgeworks. That also carries us another (small) step away from ISO, but as a logic language, rational number support should have been an obligatory part of the ISO standard IMO.

3 Likes

Yes, but as @ridgeworks correctly claims, rationals are currently too awkward to use them as the default result for 1/3, i.e.

 ?- X is 1 rdiv 3, number(X).
 false.

That may be acceptable as a kludge allowing for some sort of support for rationals, but expecting this as a result for / is not a great idea IMHO.

9 posts were split to a new topic: Using screen shots and images

2==2.0

As you probably know, there is term equivalence (==) and numerical equivalence (=:=), where the latter implies synchronizing the types and comparing according to the logic for that type. Not much changes to that when adding rational numbers.

Except that [1/2r, 1, 1.25, 3/2r] would be unchanged using sort/2.

I can submit a formal feature request based on this discussion so that at least it would be tracked.

Not sure how much more we need than this. It just waits for someone with either the money to pay someone to do this or someone sufficiently motivated with the right expertise to do the job. It would certainly be a step forward, but I’m afraid there are too many other plans on my agenda. I won’t say these are more important, but they are more important to the people that depend on SWI-Prolog and pay for it.

I have submitted issue #539 on GitHub intended as a feature request. I understand its priority depends on available resources and the priorities of real paying customers.

1 Like