6
votes

Is there any way in prolog to ask questions like 4 is 2^X, and then get back X=2? I've tried to use =, ==, =:= all without success (getting either just false or arguments not sufficiently instantiated).

4
It seems like this might be the real question you were asking in your recent, similar question. Perhaps delete the other, unless it yielded some useful answers? - Shon
I realize that now, I'm sorry for sort of double-posting! There was some really good answers in that one too, though... - user1833443

4 Answers

7
votes

With SWI-Prolog and the library(clpfd) you can ask question like that.

 ?- use_module(library(clpfd)).
%  library(apply_macros) compiled into apply_macros 0,00 sec, 39 clauses
%  library(assoc) compiled into assoc 0,03 sec, 143 clauses
% library(clpfd) compiled into clpfd 0,11 sec, 1,456 clauses
true.

 ?- 4 #= 2 * X.
X = 2.
2
votes

You can use between/3 to generate integers:

?- between(1,1000,Y), X is 2 ^ Y, X == 4.
Y = 2,
X = 4 ;
false.

?- between(1,1000,Y), X is 2 ^ Y, X == 32.
Y = 5,
X = 32 ;
false.

You can also use inf for the second argument of between/3, but without some limiting condition that will cause these queries to loop after they give their first answer. (numlist/3, is another option, in case you find it makes more sense to work with lists of numbers instead of generating a new number each time it backtracks).

Writing a general purpose predicate for this might look like,

within_range(Low-High, X=Operation) :-
    between(Low, High, V1),
    between(Low, High, V2),
    Operation =.. [_, V1, V2],
    Y is Operation,
    Y = X.

Of course, this only works with 2 place operations without considerable tweaking. Some example queries:

?- within_range(1-100, 8=X*Y).
X = 1,
Y = 8 ;
X = 2,
Y = 4 ;
X = 4,
Y = 2 ;
X = 8,
Y = 1 ;
false.

?- within_range(1-100, 8=14-X).
X = 6 ;
false.
2
votes

you can check instantiation status of a variable

% E is X^Y
% here are accepted patterns
%
% exp(+,+,-)
% exp(-,+,+)
% exp(+,-,+)
%
exp(X,Y,E) :-
     var(E)
  -> E is X^Y % note: will throw an error if either X or Y are not bound to number
  ;  var(X)
  -> compute_base_of_exp_here(Y,E,X) % note: as above, with E in place of X
  ;  var(Y)
  -> (   X =:= e % we are lucky, only natural logarithms are available as ISO builtin
      -> Y is log(X)
      ;  X =:= 2 % again, we could be lucky, log2 can be rather easy to compute...
      -> compute_log2_here(E,Y)
      ;  compute_some_complex_math_here(X,E,Y)
     )
   ; E is X^Y . % no vars? 

but this is going to be a rather difficult route, without appropriate builtins...

1
votes

Because you seem to have asked the same question before, the short answer:

No, you cannot ask this question like this.

You would have to implement this yourself. The library(clpfd) mentioned does pretty much what you need. At the least, you can try and look at the implementation for inspiration.