[mercury-users] Constraint puzzles - understanding how to go from Prolog to Mercury
Daniel Patterson
lists.mercury at dbp.mm.st
Wed Sep 7 02:36:45 AEST 2011
(sent this from the wrong address at first, hopefully this will go through and won't be a duplicate).
Wow, thanks!
I think I'll need to look closely as the eqneq module to make the second one make total sense (I can understand why it works, but not _how_ it works), but the first one is great (and, in retrospect, it is obvious).
On Sep 6, 2011, at 11:12 AM, Julien Fischer wrote:
>
> Hi,
>
> On Mon, 5 Sep 2011, Daniel Patterson wrote:
>
>> An example used in The Art of Prolog (1986, page 216) is a puzzle that
>> follows the similar pattern of "There are N people, each who have a
>> different X, Y, and Z." followed by a series of clues that should
>> completely describe a unique solution (because of the constraints
>> about uniqueness). The prolog code they showed consisted of a series
>> of logical statements that amounted to that clue, and then further
>> logical statements to extract the desired information (the solution to
>> the puzzle).
>>
>> In trying to translate this to Mercury, I ran into problems due to the
>> instantiatedness of some of the parts.
>
> The current Mercury implementation does not support partial instantiation in a
> usable form -- from memory, it allows you to create partially instantiated
> structures but can't currently fill them in.
>
> Also, note that Mercury does not (and will never) support Prolog style aliasing
> between free variables.
>
>> I looked through the section of the reference manual on Solvers and the
>> corresponding paper by Becket et al from 2006, but I couldn't tell if that
>> was even talking about the type of problem I was trying to solve.
>
> This problem can be solved using that approach provided you have a constraint
> solver that is expressive enough to describe the problem**. (As it happens,
> there is simple solver in the Mercury samples directory that is sufficient
> -- see below.)
>
> ** and efficient enough to find a solution in a reasonable amount of time.
>
>> I also looked through the Prolog -> Mercury guide, but didn't notice anything
>> that pertained to these kind of problems.
>>
>> Either way, the code I wrote (which was a simplified translation of
>> what was written in The Art of Prolog), was the following. This
>> problem involves the ranking of a programming competition between
>> three individuals from different countries who prefer different
>> sports. I've left out my attempts at writing the modes because none of
>> them worked!
>
> ...
>
>> The main problems seemed to arise from the fact that all of the predicates
>> were partially filling in the friends, so, for example, it would say that the
>> final instantiatedness was friend(ground,free,free), for example.
>>
>> Are these types of problems solvable, and if so, how? Or does someone
>> have an example of the way that they have done it (it could be any
>> similar problem, not just this one)?
>
> Here are two Mercury versions of this program: the first uses a generate-and-
> test style approach while the second uses a constrain-and-generate style
> approach using solver types.
>
> (1)
>
> :- module puzzle1.
> :- interface.
>
> :- import_module io.
>
> :- pred main(io::di, io::uo) is det.
>
> :- implementation.
>
> :- import_module list.
> :- import_module solutions.
> :- import_module string.
>
> main(!IO) :-
> solutions(problem, Solns),
> (
> Solns = [],
> io.write_string("No solutions.\n", !IO)
> ;
> Solns = [{Name, Sport}],
> io.format("The Australian is %s.\n", [s(string(Name))], !IO),
> io.format("Richard plays %s.\n", [s(string(Sport))], !IO)
> ;
> Solns = [_, _ | _],
> io.write_string("Multiple solutions?", !IO)
> ).
>
> :- type name ---> michael; richard; simon.
> :- type nationality ---> american; australian; israeli.
> :- type sport ---> tennis; basketball; cricket.
>
> :- type friend ---> friend(name, nationality, sport).
>
> :- pred problem({name, sport}::out) is nondet.
>
> problem({Name, Sport}) :-
> friend(Friend1), friend(Friend2), friend(Friend3),
> Friends = [Friend1, Friend2, Friend3],
>
> % Clue 1.
> did_better(Man1Clue1, Man2Clue1, Friends),
> name(Man1Clue1, michael),
> sport(Man1Clue1, basketball),
> nationality(Man2Clue1, american),
>
> % Clue 2.
> did_better(Man1Clue2, Man2Clue2, Friends),
> name(Man1Clue2, simon),
> nationality(Man1Clue2, israeli),
> sport(Man2Clue2, tennis),
>
> % Clue 3.
> first(ManClue3, Friends),
> sport(ManClue3, cricket),
>
> % Query: Who is the Australian?
> list.member(Q1, Friends), name(Q1, Name), nationality(Q1, australian),
>
> % Query: What sport does Richard play?
> list.member(Q2, Friends), name(Q2, richard), sport(Q2, Sport).
>
> :- pred friend(friend::out) is multi.
>
> friend(Friend) :-
> ( Nationality = american
> ; Nationality = australian
> ; Nationality = israeli
> ),
> ( Sport = tennis
> ; Sport = basketball
> ; Sport = cricket
> ),
> ( Name = michael
> ; Name = richard
> ; Name = simon
> ),
> Friend = friend(Name, Nationality, Sport).
>
> :- pred did_better(friend::out, friend::out, list(friend)::in) is nondet.
>
> did_better(A, B, [A, B, _]).
> did_better(A, C, [A, _, C]).
> did_better(B, C, [_, B, C]).
>
> :- pred first(friend::out, list(friend)::in) is semidet.
>
> first(F, [F | _]).
>
> :- pred name(friend::in, name::out) is det.
>
> name(friend(Name, _, __), Name).
>
> :- pred nationality(friend::in, nationality::out) is det.
>
> nationality(friend(_, Nationality, _), Nationality).
>
> :- pred sport(friend::in, sport::out) is det.
>
> sport(friend(_, _, Sport), Sport).
>
> :- end_module puzzle1.
>
> (2)
>
> This version of the program uses solver types, in particular it uses the
> eqneq/1 solver type that is provided by the eqneq module in the Mercury samples.
> (In the directory samples/solver_types.) The program needs to be compiled in
> a grade that supports trailing (e.g. asm_fast.gc.trseg or hlc.gc.trseg).
>
> Some caveats:
>
> (i) the search (labelling) strategy used here is pretty naive.
> (ii) explicitly posting the constraint that each friend has a different
> name, plays a different sport, etc would reduce the size of the
> search space.
> (iii) the eqneq solver is only intended as an example, while it's fine
> for small problems, it won't scale terribly well.
>
> :- module puzzle2.
> :- interface.
>
> :- import_module io.
>
> :- pred main(io::di, io::uo) is det.
>
> :- implementation.
>
> :- import_module eqneq.
>
> :- import_module list.
> :- import_module solutions.
> :- import_module string.
>
> main(!IO) :-
> solutions(problem, Solns),
> (
> Solns = [],
> io.write_string("No solutions.\n", !IO)
> ;
> Solns = [{Name, Sport}],
> io.format("The Australian is %s.\n", [s(string(Name))], !IO),
> io.format("Richard plays %s.\n", [s(string(Sport))], !IO)
> ;
> Solns = [_, _ | _],
> io.write_string("Multiple solutions?", !IO)
> ).
>
> :- type nationality ---> american ; australian ; israeli.
> :- type sport ---> tennis ; basketball ; cricket.
> :- type name ---> michael ; richard ; simon.
>
> :- type friend(Name, Nationality, Sport)
> ---> friend(Name, Nationality, Sport).
>
> :- inst friend(I) == bound(friend(I, I, I)).
>
> :- type friend_var == friend(eqneq(name), eqneq(nationality), eqneq(sport)).
> :- type friend_vars == list(friend_var).
>
> :- type friend_value == friend(name, nationality, sport).
> :- type friend_values == list(friend_value).
>
> :- pred friend(friend_var::out(friend(any))) is det.
>
> friend(Friend) :-
> eqneq.new(Name),
> eqneq.new(Nationality),
> eqneq.new(Sport),
> Friend = friend(Name, Nationality, Sport).
>
> :- pred problem({name, sport}::out) is nondet.
>
> problem({Name, Sport}) :-
> friend(FriendVar1), friend(FriendVar2), friend(FriendVar3),
> FriendVars = [FriendVar1, FriendVar2, FriendVar3],
>
> % Clue 1.
> did_better(Man1Clue1, Man2Clue1, FriendVars),
> name(Man1Clue1, michael),
> sport(Man1Clue1, basketball),
> nationality(Man2Clue1, american),
>
> % Clue 2.
> did_better(Man1Clue2, Man2Clue2, FriendVars),
> name(Man1Clue2, simon),
> nationality(Man1Clue2, israeli),
> sport(Man2Clue2, tennis),
>
> % Clue 3.
> first(ManClue3, FriendVars),
> sport(ManClue3, cricket),
>
> % Search.
> label(FriendVars, FriendValues),
>
> % Query: Who is the Australian?
> list.member(friend(Name, australian, _), FriendValues),
>
> % Query: What sport does Richard play?
> list.member(friend(richard, _, Sport), FriendValues).
>
> :- pred did_better(friend_var::out(friend(any)), friend_var::out(friend(any)),
> friend_vars::in(list(friend(any)))) is nondet.
>
> did_better(A, B, [A, B, _]).
> did_better(A, C, [A, _, C]).
> did_better(B, C, [_, B, C]).
>
> :- pred first(friend_var::out(friend(any)),
> friend_vars::in(list(friend(any)))) is semidet.
>
> first(F, [F | _]).
>
> :- pred name(friend_var::in(friend(any)), name::in) is semidet.
>
> name(friend(NameVar, _, _), Name) :-
> eqneq.bind(NameVar, Name).
>
> :- pred nationality(friend_var::in(friend(any)), nationality::in) is semidet.
>
> nationality(friend(_, NationalityVar, _), Nationality) :-
> eqneq.bind(NationalityVar, Nationality).
>
> :- pred sport(friend_var::in(friend(any)), sport::in) is semidet.
>
> sport(friend(_, _, SportVar), Sport) :-
> eqneq.bind(SportVar, Sport).
>
> :- pred label(friend_vars::in(list(friend(any))), friend_values::out) is nondet.
>
> label([], []).
> label([FriendVar | FriendVars], [FriendValue | FriendValues]) :-
> label_friend(FriendVar, FriendValue),
> label(FriendVars, FriendValues).
>
> :- pred label_friend(friend_var::in(friend(any)), friend_value::out) is nondet.
>
> label_friend(Var, Value) :-
> Var = friend(NameVar, NationalityVar, SportVar),
> ( NameValue = michael
> ; NameValue = richard
> ; NameValue = simon),
> eqneq.bind(NameVar, NameValue),
> ( NationalityValue = american
> ; NationalityValue = australian
> ; NationalityValue = israeli
> ),
> eqneq.bind(NationalityVar, NationalityValue),
> ( SportValue = tennis
> ; SportValue = basketball
> ; SportValue = cricket
> ),
> eqneq.bind(SportVar, SportValue),
> Value = friend(NameValue, NationalityValue, SportValue).
>
> :- end_module puzzle2.
>
> Julien.
> --------------------------------------------------------------------------
> mercury-users mailing list
> Post messages to: mercury-users at csse.unimelb.edu.au
> Administrative Queries: owner-mercury-users at csse.unimelb.edu.au
> Subscriptions: mercury-users-request at csse.unimelb.edu.au
> --------------------------------------------------------------------------
--------------------------------------------------------------------------
mercury-users mailing list
Post messages to: mercury-users at csse.unimelb.edu.au
Administrative Queries: owner-mercury-users at csse.unimelb.edu.au
Subscriptions: mercury-users-request at csse.unimelb.edu.au
--------------------------------------------------------------------------
More information about the users
mailing list