Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request (^)/2 for rationals in SWI-Prolog. #185

Closed
ghost opened this issue Dec 4, 2016 · 17 comments
Closed

Feature request (^)/2 for rationals in SWI-Prolog. #185

ghost opened this issue Dec 4, 2016 · 17 comments

Comments

@ghost
Copy link

ghost commented Dec 4, 2016

I like very much that SWI-Prolog provides rationals via the operator rdiv/2. We can do things such as the following:

?- X is 1 rdiv 2+1 rdiv 3.
X = 5 rdiv 6.

When using rationals we can use all the other operators (+)/2, (-)/2 and (*)/2 in is/2 unchanged. This lead me to the expectation that (^)/2 can also be used unchanged:

?- X is (2 rdiv 3)^5.
X = 0.13168724279835387.

As can be seen this is not the case. My expectation was:

?- X is (2 rdiv 3)^5.
X = 32 rdiv 243.

I know that the operator (^)/2 was only recently required the ISO Corr.2. Would it be possibly to extend this operator to also work for exactly rationals?

@wouterbeek
Copy link
Contributor

@jburse Seems that you already have part of the code ready. Can you contribute this enhancement yourself as per a pull request?

@triska
Copy link
Member

triska commented Jul 2, 2017

Importantly, SWI-Prolog/packages-clpqr#1 may also be related to this issue!

@JanWielemaker
Copy link
Member

Pushed a4bfaf5, which add support for rationals to ^ and **.
This indeed also fixes the clpq bug. Closing both.

@triska
Copy link
Member

triska commented Jul 3, 2017

I am reopening this issue because it is currently not fully fixed.

I agree with Jan that R^K should yield a rational number if R is a rational number and K is an integer, also if K is negative.

For example, in SICStus Prolog, we have:

| ?- { X = 3/2, Y = X^(-1) }.
X = 3/2,
Y = 2/3 ? ;
no

@triska triska reopened this Jul 3, 2017
@triska
Copy link
Member

triska commented Jul 3, 2017

I hope that floats as we know them today go away rather sooner than later!

For comparison, here is the query with SWI:

?- { X = 3/2, Y = X^(-1) }.
false.

This means that SWI behaves different from SICStus for the important domain of CLP(Q).

I have at least one program where this difference is critical, so I have filed this as SWI-Prolog/packages-clpqr#2.

@triska
Copy link
Member

triska commented Jul 3, 2017

With SICStus, I get:

| ?- X is 5^(-2).
! Type error in argument 2 of (is)/2
! expected a float, but found 5
! goal:  _187 is 5^ -2

This means that doing something correct, namely, yielding:

?- X is 5^(-2).
X = 1 rdiv 25.

would not conflict with something that SICStus computes.

As an aside, it would be slightly odd to now appeal to the ISO standard on this particular issue (of all things!), where it is pretty clear what should be computed, and yet completely ignore the standard on much more fundamental issues. Please also take into account that the standard says nothing about rational numbers at all.

With SWI-Prolog, we currently get:

?- X is 5^(-2).
X = 0.04.

That's quite correct, right? Yes, quite:

?- X is 5^(-2),
   format("~32f", [X]).
0.04000000000000000083266726846887

Behold the pioneering technology from 70 years ago!

@triska
Copy link
Member

triska commented Jul 3, 2017

I get, with SICStus:

| ?- { X = 5^(-2) }.
X = 1/25 ? ;
no

and with SWI:

?- { X = 5^(-2) }.
false.

Consider now a case with SWI that does not intersect in any way with the core standard:

?- X is (1 rdiv 5)^(-1).
X = 5.0.

Expectation: X = 5.

@triska
Copy link
Member

triska commented Jul 3, 2017

Yes, that would be great, thank you!

@triska triska closed this as completed Jul 3, 2017
@JanWielemaker
Copy link
Member

I think it tells me that we should

  • Make rational numbers a primary citizen rather than a hack using compound terms.
  • Have all operations between integers and rationals return rationals if this is the
    mathematically correct result. This is not ISO, but advocated by several people
    among whom Richard O'Keefe.

Right now, negative exponentials are supported as in

?- X is (1 rdiv 4)^(-2).
X = 16.

This is compatible with e.g.

?- A is 1 rdiv 4/4.
A = 1 rdiv 16.

But, this is broken because N rdiv 1 evaluates to an integer, causing float conversion if the
division does not work. The below is not desirable:

?- A is 4 rdiv 1/3.
?- A = 1.3333333333333333.

You can actually force it, as in

?- A is rational(4 rdiv 1)/3.
A = 4 rdiv 3.

The second issue is clp(Q), which in part relies on SWi-Prolog's rational support. This reliance seems
incomplete, which may be either SWI-Prolog or clp(Q) issues or both.

For now I'll leave it as is, but this should be resolved at some point.

@JanWielemaker
Copy link
Member

rdiv as operation and term was a quick hack. There is lots of stuff you'd expect to work but that do not.
For example:

?- A is 3 rdiv 4, number(A).
false.  % YUK!

We can only fix all these if we turn rationals into a proper atomic data type with accompanying syntax. That is probably not very hard, but requires some restructuring in the basic data representation to accommodate an additional basic type.

The second simple idea is that all basic mathematical functions that have integer or rational arguments and an integer or rational answer returns this exact answer. So, notably / will return a rational number if both arguments are integer/rational. If you want float division, just write A is float(X)/Y. Combine this with SWI-Prolog's standard order of terms that respects numerical ordering and the world behaves pretty much as you would intuitively expect it to while being logically and mathematically correct.

One issue is that I do not want more dependency on the LGPL GMP library ...

@JanWielemaker
Copy link
Member

JanWielemaker commented Jul 4, 2017

As Richard has argued many times, (ISO) Prolog arithmetic is flawed. Floats are fine for functions that inherently are about real numbers (in the math sense). Otherwise they are a poor match for any high level languages and particularly so for a logic based language. Switching to an approximation should be really the last resort, in particular so because unification is based on strict equivalence. If you want scruffy arithmetic, just cast one or more of the operands to a float using float(X) and all subsequent arithmetic will use floats as we cannot get exact results after having introduced an approximation.

ISO should have demanded unbounded integer and rational number support from the start. Well, possibly this was asking too much back then, but it should have corrected this error long ago.

@JanWielemaker
Copy link
Member

Automatic casting never turns a float into a rational, as floats are never converted into integers right now. Only explicit casting does so, where you control the rounding. All this is not rocket science.

@JanWielemaker
Copy link
Member

You use the opportunity that Prolog is a dynamically typed language. This issue' will go away if 3/9` becomes a rational as integers are just a pure subset of rationals, while integers are a pure subset of math real numbers, but not of floats.

@ghost
Copy link
Author

ghost commented Jan 24, 2020

I think the feature was incorrectly implemented. I get for example
this buggy result:

   ?- X is (13 rdiv 4 - (1 rdiv 2)*(1 rdiv 2))^(-1).
   X = 1 rdiv 3.

   ?- Y is 13 rdiv 4 - (1 rdiv 2)*(1 rdiv 2), X is Y^(-1).
   Y = 3,
   X = 0.3333333333333333.

It would be better if we had two pairs ideally like (/)/2 and rdiv/2, with
a clear definition which one returns float and which one returns rational numbers.

@ghost
Copy link
Author

ghost commented Jan 24, 2020

I am not sure, maybe (**)/2 and (^)/2 could play this role.
The expected outcome would then be:

We could reserve (**)/2, so that this
operator only returns float:

   ?- X is (13 rdiv 4 - (1 rdiv 2)*(1 rdiv 2))**(-1).
   X = 0.3333333333333333.

   ?- Y is 13 rdiv 4 - (1 rdiv 2)*(1 rdiv 2), X is Y**(-1).
   Y = 3,
   X = 0.3333333333333333.

And (^)/2 would always try rational numbers:

   ?- X is (13 rdiv 4 - (1 rdiv 2)*(1 rdiv 2))^(-1).
   X = 1 rdiv 3.

   ?- Y is 13 rdiv 4 - (1 rdiv 2)*(1 rdiv 2), X is Y^(-1).
   Y = 3,
   X = 1 rdiv 3. 

@JanWielemaker
Copy link
Member

This issue has been mentioned on SWI-Prolog. There might be relevant details there:

https://swi-prolog.discourse.group/t/future-of-support-for-rational-numbers/1779/14

@ghost
Copy link
Author

ghost commented Feb 18, 2020

I found a further argument to give different roles to (**)/2 and (^)/2.

For example ECLiPSe Prolog gives this result, since it does not
normalize rational numbers to ordinary integers where possible:

   ECLiPSe Version 7.0 #52 (x86_64_nt)

   [eclipse 1]: X is 10_1^(-5).
   X = 1_100000
   Yes (0.00s cpu)

   [eclipse 2]: X is 10^(-5).
   X = 1.0000000000000006e-5
   Yes (0.00s cpu)

On the other hand I get:

   SWI-Prolog (threaded, 64 bits, version 8.1.22)

   ?- X is 10r1^(-5).
   X = 1.0000000000000006e-5.

So from the data type of the argument of (^)/2 it cannot
be seen what to do, and we possibly cannot get an exact

result like 1r100000. Except we would give this role to (**)/2 and
reserve (^)/2 to give rational numbers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants