2

I'm a beginner to Prolog and have two requirements:

  1. f(1) = 1

  2. f(x) = 5x + x^2 + f(x - 1)

rules:

f(1,1). f(X,Y) :- Y is 5 * X + X * X + f(X-1,Y).

query:

f(4,X).

Output:

ERROR: is/2: Arguments are not sufficiently instantiated

How can I add value of f(X-1)?

mat
  • 40,498
  • 3
  • 51
  • 78
mdo123
  • 1,757
  • 3
  • 16
  • 34

2 Answers2

5

This can be easily solved by using auxiliary variables.

For example, consider:

f(1, 1).
f(X, Y) :-
        Y #= 5*X + X^2 + T1,
        T2 #= X - 1,
        f(T2, T1).

This is a straight-forward translation of the rules you give, using auxiliary variables T1 and T2 which stand for the partial expressions f(X-1) and X-1, respectively. As @BallpointBen correctly notes, it is not sufficient to use the terms themselves, because these terms are different from their arithmetic evaluation. In particular, -(2,1) is not the integer 1, but 2 - 1 #= 1 does hold!

Depending on your Prolog system, you may ned to currently still import a library to use the predicate (#=)/2, which expresses equality of integer expressesions.

Your example query now already yields a solution:

?- f(4, X).
X = 75 .

Note that the predicate does not terminate universally in this case:

?- f(4, X), false.
nontermination

We can easily make it so with an additional constraint:

f(1, 1).
f(X, Y) :-
        X #> 1,
        Y #= 5*X + X^2 + T1,
        T2 #= X - 1,
        f(T2, T1).

Now we have:

?- f(4, X).
X = 75 ;
false.

Note that we can use this as a true relation, also in the most general case:

?- f(X, Y).
X = Y, Y = 1 ;
X = 2,
Y = 15 ;
X = 3,
Y = 39 ;
X = 4,
Y = 75 ;
etc.

Versions based on lower-level arithmetic typically only cover a very limited subset of instances of such queries. I therefore recommend that you use (#=)/2 instead of (is)/2. Especially for beginners, using (is)/2 is too hard to understand. Take the many related questions filed under  as evidence, and see for declarative solutions.

mat
  • 40,498
  • 3
  • 51
  • 78
  • concerning your code, if called in `f(+,-)` mode, is there a constraint system implementation capable of running it in [O(1) stack space](https://en.wikipedia.org/wiki/Tail_call#Tail_recursion_modulo_cons)? – Will Ness Dec 12 '16 at 19:57
  • Since the recursive call is in a tail position, and no choice points are left when the recursive goal is invoked, this runs in O(1) (local) stack space in all implementations that perform this optimization. For example, in SWI-Prolog, you can verify this by adding the goals `statistics(local_stack, L), portray_clause(L)` at the beginning of the second clause of `f/2`. This also holds in `f(+,+)` and *also* in `f(-,-)` modes! – mat Dec 12 '16 at 20:19
  • Note though that accumulated constraints are stored on the *global* stack! If you do not want this, you can always reorder the goals and have exactly the same properties as with lower-level arithmetic regarding memory consumption. – mat Dec 12 '16 at 20:26
  • okay, thanks, but I meant it more generally, sorry for being unclear. so, there is an "accumulation" of constraints, i.e. more and more get created? The ideal I'm after is the automatic "collapsing" of constraints, in effect getting the accumulator-based iterative calculation (re: "modulo-cons"; `+` is an associative constructor too, just as is list-append), with *"eager"* constraints. Yes, I can always write this with `is` and an additional accumulating argument by hand; but can the *constraints* do this *for* me? – Will Ness Dec 12 '16 at 21:15
  • 1
    That's a good example for where we would need more user input to improve CLP(FD) systems: I think what you suggest may be doable and worth implementing to cover at least a group of cases that often arise in practice. So far, I have seen no system that does this. Once we see more usage of CLP(FD) constraints for arithmetic, implementors will look more seriously into such cases, to make them more efficient. – mat Dec 12 '16 at 21:40
  • thank you for your replies. I think, if such a clear benefits could be shown, and advertised, more and more people would get convinced. :) the full promise of TRMC ([from 1974](http://www.cs.indiana.edu/cgi-bin/techreports/TRNNN.cgi?trnum=TR19) no less) is yet to be realized; e.g. Haskell's Foldable (into a Monoid) would seem to suggest such a transformation too, but might still be merely a suggestion (to a compiler) for now, AFAIK. there, a goal could be the reduction of total number of thunks; with CLP systems, the reduction of total number of active constraints. – Will Ness Dec 13 '16 at 09:08
  • Yes, definitely. We must take this step by step. First, get people to use constraints instead of low-level arithmetic. Next, improve the constraints by dedicated algorithms that solve issues that will then be recognized as frequently arising in practice, and important for users. At least with the first issue, everyone who is interested in these topics can help every day on Stackoverflow, by promoting these newer mechanisms instead of downvoting answers that advertise them, as is currently still frequently the case. Thank you for these pointers! – mat Dec 13 '16 at 11:07
4

The issue is that you are trying to evaluate f(X-1,Y) as if it were a number, but of course it is a predicate that may be true or false. After some tinkering, I found this solution:

f(1,1).
f(X,Y) :- X > 0, Z is X-1, f(Z,N), Y is 5*X + X*X + N.

The trick is to let it find its way down to f(1,N) first, without evaluating anything; then let the results bubble back up by satisfying Y is 5*X + X*X + N. In Prolog, order matters for its search. It needs to satisfy f(Z,N) in order to have a value of N for the statement Y is 5*X + X*X + N.

Also, note the condition X > 0 to avoid infinite recursion.

BallpointBen
  • 9,406
  • 1
  • 32
  • 62
  • Thanks @BallpointBen but it gives me out of global stack memory error when I try that solution. I also think my base case was wrong and changed it to "f(1,Y) :- Y is 1." to meet the requirement of f(1) = 1. – mdo123 Dec 11 '16 at 07:07
  • Actually, Prolog can infer `Y=1` on it's own. – BallpointBen Dec 11 '16 at 07:09
  • but what about the out of memory error, any ideas how to correct that @BallpointBen? Or any sources I can read you can recommend to understand? – mdo123 Dec 11 '16 at 07:29