views:

282

answers:

3

I have a prolog predicate:

Add( [A|B] , Answer ) :-
    ...
    ~ Add everything in the list to come up with answer
    ...

I would now like to implement AddUnique that would return unique values for everything in the list except when I give it the variable twice.


Here are somethings that are logically equivalent:

?- AddUnique([A, B, C], 10). is equivalent to: ?- Add([A, B, C], 10), A != B, B != C, A != C.

And:

?- AddUnique([A, B, B], 10). is equivalent to: ?- Add([A, B, B], 10), A != B.

Also:

?- AddUnique([A, B, B], 10). is NOT equivalent to: ?- Add([A, B, B], 10), A != B, B!=B.


If ?- AddUnique([A,B,C,D], 4). is given it should return false since it cannot come with unique positive integers that add to four.

If ?- AddUnique([A,A,A,A], 4). is given it should return A=1.


Question: How can I move the A != B, B != C, A != C. logic inside the predicate without doing something like this A != A?

+1  A: 

Khm... You should understand that doStuff(A,B,C,D) and doStuff(A,A,B,B) means. First is going to unify values A .. D with appropriate values which makes doStuff/4 reachable goal. And second is equal to A=B, C=D, doStuff(A,B,C,D) and doStuff(A,B,C,D), A=B, C=D (but last variant probably will cause backtracking). So I hope you understand that unique/1 shouldn't be done inside doStuff/4, because it's outside restriction. So you shoulad use doStuff(A,B,C,D), unique([A,B,C,D]) and doStuff(A,A,B,B), unique([A,B]).

I wonder how you read A is not B... Anyway you can define unique/1 as

not_unique([H|T]):- member(H, T) ; not_unique(T).
unique(L):- not(not_unique(L)).
ony
@Ony, thanks for the response, unfortunately I do not understand. I have rewritten my question to improve clarity. I also had a typo that was probably causing confusion. Thanks for your responce maybe now my question will be more clear.
sixtyfootersdude
Ehh... It's hard to explain something when it looks for you too obvious. If `addUnique([A,B], 4)` will be reached only when `A \= B`, then `addUnique([A,A], 4)` will never be reached because `A \= A` is always false no metter what `A` is actually. What I'm trying to say is that you want something that makes no sense. You want "A = B" and "A != B". Probably that's because you understand `addUnique([A,B,B], 10)` as a magic that produces result `A=.., B=..` but not as `addUnique/2` takes two arguments first of which is list of three elements, where two last elements of list have the same value.
ony
@Ony, Thanks for the response. I understand what you are saying. What I am asking for is contradictory. I think that I need to look at the problem differently. Do you have any suggestions?
sixtyfootersdude
No you are not understand. Your two "if" is totally different goals. First says "I want to reach goal `addUnique(L, 4)` where list `L` consist of 4 unique values". And second says "I want to reach goal `addUnique(L, 4)`, where `L` is a list of 4 equal values". You can't do both by simple goal `addUnique(L, 4)` at least until computer will be able to read your mind and decide when you want first and when you want second. Once you'll get inside `addUnique/2` those combination of values hiding under names in lists `[A,B,C,D]` and `[A,A,A,A]` will disappear (as in many other languages).
ony
Huh, Thanks for the response ony. I guess that there is no predicate `sameVariable(A,B)`? I am going to talk to my prof today unless something becomes suddenly clear to me, I will mark your answer as the accepted answer. THanks for all your help.
sixtyfootersdude
What do you think of my solution?
sixtyfootersdude
A: 

Given your description of the addUnique/2 predicate, constraint logic programming can be used to implement a solution. This is far from beginner stuff, but I'll post an explanation anyway.

Firstly, it might be worthwhile looking up what constraint logic programming is, and an how to use an implementation (e.g., SWI-PL clpfd). Basically, constraint logic programming (particularly the finite domain solver) will allow you to specify the following constraints over your variables on the input list to addUnique/2:

  1. How the variables on your input list can bind to certain numeric values (i.e., integers from 0 up to and including a specified value)
  2. How the different variables on your input list cannot simultaneously bind to the same value (i.e., != where different)
  3. How the sum of the variables in the input list plus any numbers must add up to a specified value (i.e., the maximum value that variables can take on in 1, above).

Together, these specifications will allow the underlying constraint solver to automatically determine what permissible values the variables can simultaneously take on given the above constraints, giving you your solutions (there may be several, one, or none).

Here is a solution using the aforementioned constraint solver in SWI-PROLOG (the clpfd solver):

:- use_module(library(clpfd)).  % import and use the constraint solver library

addUnique([A|As], Val) :-
    unique_vars([A|As], UVs),  % determine all unique variables
    UVs ins 0..Val,            % (1) domain of all unique variables is 0 to Val
    pairwise_inequ(UVs),       % (2) all unique variables are pairwise !=
    construct_sum_constr(As, Val, A, Program),  % (3) construct the sum constraint
    Program,              % assert the sum constraint
    label(UVs).           % label domains to enumerate a solution (backtracks)

% predicate to return a list of unique vars, if present
unique_vars([], []).
unique_vars([V|Vs], [V|Uniq]) :-
    var(V),
    \+ var_memberchk(V, Vs), !,
    unique_vars(Vs, Uniq).
unique_vars([_|Vs], Uniq) :-
    unique_vars(Vs, Uniq).

% predicate to test if a variable appears in a list (possibly including variables)
var_memberchk(V0, [V1|_]) :- 
    V0 == V1, !.
var_memberchk(V0, [_|V1s]) :- 
    var_memberchk(V0, V1s).

% create constraints that assert each in the input list != to each other
pairwise_inequ([]).
pairwise_inequ([V|Vs]) :-
    map_inequ(Vs, V),
    pairwise_inequ(Vs).

% predicate to pairwise assert inequality between all list members
map_inequ([], _).
map_inequ([V1|V1s], V0) :-
    V0 #\= V1,   % the inequality constraint
    map_inequ(V1s, V0).

% predicate to construct a summation constraint, whereby all variables in the 
% input list are constructed into a sum with +/2 and finally equated to a value
construct_sum_constr([], Val, Sum, (Sum #= Val)).
construct_sum_constr([V|Vs], Val, Sum, Program) :-
    construct_sum_constr(Vs, Val, (V + Sum), Program).

Running this code, e.g., gives you:

?- addUnique([A,B,B], 6).
A = 0,
B = 3 ;
A = 4,
B = 1 ;
A = 6,
B = 0.

; enumerates the next solution for permissible bindings between variables. Note that A and B never take on the same value, as required, but all occurrences in the input list will always sum to 6. Another query:

 ?- addUnique([A,A,A],4).
false.

The result is failure here because no single integer could be found to bind to A that, when summed up, totaled 4, whereas:

 ?- addUnique([A,A,A,A],4).
A = 1.

...as expected. Also, you wanted to try:

?- addUnique([A,B,C,D],4).
false.

Again, the result is failure here because all the variables A, B, C and D were all asserted to be different, and cannot all bind to 1.

EDIT ps. ony also wanted to try:

?- addUnique([A,A,A,1],4).
A = 1.

A simple modification to the code above ensures that only variables are used when calling ins to assert domains (and not any numbers in the input list).

sharky
try `add([A,A,A,1],4).` or `add([A,A,A,B],4), A=B.` and you'll get "false"
ony
@ony, this is the expected behavior of the code I've suggested, as per the specification in the question. I did not cater for anything other than lists of variables on the input, so `add([A,A,A,1],4)` will of course fail. Additionally, `add([A,A,A,B],4), A=B.` will also fail expectedly, since in the specification, different variables on the input list (here, `A` and `B`) are related with inequality, meaning the unification between `A` and `B` will always force failure on any solution...
sharky
Thanks for the responce rati, I think that your answer works and I have voted for it. I think however that my answer is simpler so I have marked it as the accepted answer.
sixtyfootersdude
@rati, your predicate changes the logic depending on what is already known. in your case you have `not(add([A,B],2)), A=B, add([A,B],2))` to be reachable target even that there is `not(G), G.` in the same conjunctions which is absurd. When you saying `A=B` you are making more strict assumptions which make predicate produce no more solution than it produces without `A=B`. Read the documentation on `==/2` and you'll find `+Term1 == +Term2` which means that both terms should be fully instantiated.
ony
@ony, you are incorrect. Understand that when I posted that _"the different variables on your input list cannot simultaneously bind to the same value"_, that making an assertion such as `A=B` in conjunction with `addUnique([A,B],2)` as you suggest will *always force failure* given the CLP solution I have put forward. I suggest you make an attempt to understand what I have posted before down-voting and trashing it.
sharky
@rati, I understand it and trying to say that this is bad approach. Imagine that space of solutions of `addUnique([A,B],2)` is X, than `A=B, addUnique([A,B],2)` should be subset of X because it is more strict, but in your case that leads to the different meaning of `addUnique/2`. When you going deeper through conjunctions set of your solutions whould decrease. `A#=B+1, A#=C+1, add([B,C],2)` will also be false even that `B` and `C` is bound to the same value. You relay on behavior which exists only as side effect which is totally wrong in declarative programming.
ony
Oh dear, it seems I have been **beaten with experience** ! :-O~~~
sharky
@rati, nothing related with exp. or beating. I just don't want people to miss some moments of this language that can change their view at programming in other languages (yes, I didn't used Prolog for earning money or anything else). When someone looking for solution of small "problem" without trying to reconsider the source of that problem even when language resists to solve it in the way you "want it"... I feel that I need to say something. Since Prolog changed my way of thinking I did quit using `repeat/0` and `fail/0` and trying to fit it in any of my prev. languages.
ony
A: 

This is the solution that I came up with. It will only assign the input to be numbers less than ten but works great for that!

addUnique( A, Answer ) :- 
    used(A,[0,1,2,3,4,5,6,7,8,9],_),
    add(A,Answer).

add( [A|B] , Answer ) :-
    ~ Add everything in the list to come up with answer ~.


% ================================
% Ensures that all variables are unique.  
% ================================

% Base case: Assigned variables unique values
used([], Nin, Nin).

% Have already assigned a value to this variable
used([A|B], Nin, Nout) :-
        integer(A),
        helper(B,Nin,Nout).

% Have not assigned a value to this variable yet
% Assign it and remove it from the list.  
used( [A|B] , Nin, Nout) :-
        member(A,Nin),
        delete(Nin,A,Temp),
        helper(B,Temp,Nout).
sixtyfootersdude
Predicates to check if "variable" is free or already bound should change strategy for deriving right unifications for other "variables" (i.e. speed up, or make possible to derive them). The same for dynamic predicates - they can be used to speed-up something, but they shoudn't be used as trigger of changing behaviour of something.
ony
ony