Algorithmically pick which rule to apply? - prolog

Let say I have the following rules :
?- assert(test(5,2) :- true).
true.
?- assert(test(5,3) :- true).
true.
?- assert(test(5,4) :- true).
true.
?- test(5,K).
K = 2 ;
K = 3 ;
K = 4.
?- findall(K,test(5,K), L).
L = [2, 3, 4].
I can find all the solutions. But I want something different.
In this scenario "5" is the condition and the second term K is a SCORE.
I want to find all the rules that match "5", but be able to pick which one to execute,
based on the SCORE. Currently the max score "4".
Ideally I want to get the list of all the matching rules and pick randomly one with higher score.
This means I need to get back the rules as a structure, so that I can pick algorithmically one of them.
How can I do that ?
I want to simulate Production system (IF-THEN rules), where every rule have a score. The head of the rule should play the IF part.
The THEN part should not be executed until rule is selected based on the score !

If I've understood your problem correctly, the starting point for you would be using clause/2 predicate in conjunction with findall to get all possible scores, then find maximum and call your test/2 predicate with its value:
findall(Score, clause(test(5, Score), Body), Scores),
max_member(MaxScore, Scores),
test(5, MaxScore).
PS: I am assuming that you use Swi-Prolog.

Related

Prolog - confused about return results of recursive rule

I'm playing around with recursion in Prolog, and I'm confused. I am trying to write rules that can determine if a number is even or odd. I know that there are other stackoverflow questions about this, but I don't care about having a working solution, I am more interested in knowing why mine doesn't work.
Here are my rules:
even(0).
even(N) :- N>0, N1 is N-1, odd(N1).
odd(N) :- N>0, N1 is N-1, even(N1).
When I query even(0), I get returned 2 results. The first result is true, the 2nd is false. This also happens with odd(1), even(2), odd(3), etc. Why am I getting 2 return results? Shouldn't I just get 1?
When you query even(0), it succeeds as you have seen. But you've also seen it prompts you for more results because it left a choicepoint, which is a place in the logic where Prolog decides it can come back and explore other alternatives for a potentially successful query. Upon going back to the choicepoint and attempting to find more solutions, it does not find more, so it comes back "false" since it found no more solutions. So it did just find one solution, but the choice point caused backtracking after which it found no additional solutions. This is the case with your other successful queries as well.
You'll note that if you make a more general query, it gives an error (example taken from GNU Prolog):
| ?- even(N).
N = 0 ? ;
uncaught exception: error(instantiation_error,(>)/2)
| ?-
This is because you are using specific arithmetic expression operators that require that the variables be instantiated. These are relational operators like (>)/2 and the is/2 operator. You can make the solution more relational by using the CLP(FD) operators which are designed for reasoning with integers:
even(0).
even(N) :-
N #> 0,
N1 #= N-1,
odd(N1).
odd(N) :-
N #> 0,
N1 #= N-1,
even(N1).
Then you get a more general solution, which is more complete and more useful:
| ?- even(N).
N = 0 ? ;
N = 2 ? ;
N = 4 ? ;
N = 6 ? ;
...
| ?- odd(N).
N = 1 ? ;
N = 3 ? ;
N = 5 ? ;
N = 7 ?
...
If you know there is at most one answer, or if you only care about the first possible answer, you can use once/1 (examples taken from SWI Prolog here):
2 ?- even(2).
true ;
false.
3 ?- once(even(2)).
true.
4 ?- even(N).
N = 0 ;
N = 2 ;
N = 4 ;
...
5 ?- once(even(N)).
N = 0.
6 ?-
As expected, once(even(N)) terminates after finding the first solution.
The return values you have are correct. The point is how Prolog is evaluating predicates. When you query i.e.
even(2)
Prolog firstly evaluate that this predicate is Yes / true. When going through next possibility it return No / false, because it cannot find any more.
To check what exactly is performed under the hood go to:
https://swish.swi-prolog.org
on the left side type rules (i.e. odd/even) and on the query window type like 'odd(2)', but just before running click 'solutions'->'debug(trace)'. It will let you go step by step of what Prolog is doing.
Also please take a look at the successor example in tutorial below.
http://www.learnprolognow.org/lpnpage.php?pagetype=html&pageid=lpn-htmlse9
from a link above, try such code for a reversed example:
numeral(0).
numeral(succ(X)) :- numeral(X).
Now evaluating numeral(0) for the first time return succ(0), another time succ(succ(0)) etc.
Each time next evaluation brings another possible solution for a query.
What Prolog does is a "depth-first search", which means Prolog walks through a decision tree until it either finds a solution and succeeds OR it fails. In either case a process called "backtracking" kicks in. Along the way, going through the tree of choices, Prolog keeps track of where it has MULTIPLE possible routes that could potentially satisfy the goal. Such a point in the decision tree is called a "choice point".
This means Prolog will
search ->
succeed or fail ->
go back to the last choice point ->
repeat until all possible paths have been tried
Given your program:
even(0).
even(N) :- N>0, N1 is N-1, odd(N1).
odd(N) :- N>0, N1 is N-1, even(N1).
We can clearly see TWO ways to satisfy even(0).. The first is the fact even(0) and the second is the recursive rule even(N). Prolog reads top to bottom, left to right so the first encounter is even(0). which is true, and the second is even(N). which goes through N-1 making the result N1 = -1, then goes through odd(N) making the result N1 = -2, which in unequal to even(0). so it fails and then calls even(N) again. Your specific version of Prolog likely sees that it is an infinitely recursive predicate and doesn't even try to satisfy it even though it's a valid declarative path , but not a valid procedural path.
If you know that the mode is (+), you can place a cut,
to suppress the unnecessary choice point:
even(0) :- !.
even(N) :- N > 0, N1 is N-1, odd(N1).
odd(N) :- N > 0, N1 is N-1, even(N1).
The above is better than wrapping a query with
once/1 since it allows the Prolog interpreter to
use last call optimization. There is now no more
problem with an extra choice point:
?- even(3).
false.
?- even(4).
true.
But if the mode is not fixed, you have to be more careful
with cuts. Probably write a separate carefully crafted
predicate for each mode.
CLP(FD) itself seems not to help, it cannot avoid the need
to place cuts, but can sometimes avoid the need to code
different variants for different modes.

Predicate about a list of lists

I'm trying to create a predicate that receives a list of lists and returns a list of lists containing all the unitary lists (lists whose length is 1) from the first list, however it is not working. This is what I created:
elimina_listas_nao_unitarias_lista_de_listas([[A]|T],N_List):-
length([A], 1),
N_List is [H|N_List_T],
elimina_listas_nao_unitarias_lista_de_listas(T, N_List_T).
elimina_listas_nao_unitarias_lista_de_listas([[A]|T], N_List):-
length([A], X),
X > 1,
elimina_listas_nao_unitarias_lista_de_listas(T, N_List2).
Thi is what it should do:
elimina_listas_nao_unitarias_lista_de_listas([[1,2],[1,2,3],[3]], [3])
elimina_listas_nao_unitarias_lista_de_listas([[1,2],[1,2,3],[3,4,5]], [])
It is retuning false currently everytime
Let's take a look at your first rule. The first goal always succeeds, since you are asking if a list with a single element is of length 1. Just try it at the prompt:
?- length([A], 1).
true
Instead, you probably want to have a variable without the brackets in the head of the first list (e.g. [L|Ls]) and ensure that it is a list of length 1:
?- length(L,1).
L = [_A]
The same goes for the first list in the head of your second rule and its first goal. In your second goal you are trying to evaluate [H|N_List_T] as an arithmetic expression with is/2 such that N_List holds the value. Besides the fact that this doesn't make sense, you can try that at the prompt and see how this goal can't succeed:
?- N_List is [H|N_List_T].
ERROR!!
TYPE ERROR- string must contain a single character to be evaluated as an arithmetic expression: expected evaluable term, got [_131245|_131246]
Instead, you want to unify the two terms:
?- N_List = [H|N_List_T].
N_List = [H|N_List_T]
However, you can get rid of this goal entirely if you write [H|N_List_T] as the second argument in the head of the rule. Additionally, you might want the unitary list L in the head of the second list instead of the variable H. Furthermore you are missing a case, namely the first list being []. In that case the second list is empty as well, since the empty list clearly does not contain any unitary lists. Finally, I would note that it might enhance the readability of your code if you picked a somewhat simpler and more declarative name, say listas_unitarias/2. Putting all this together, you might end up with a predicate like this:
listas_unitarias([],[]).
listas_unitarias([L|Ls],[L|Ss]) :-
length(L,1),
listas_unitarias(Ls,Ss).
listas_unitarias([L|Ls],Ss) :-
length(L,X),
dif(X,1),
listas_unitarias(Ls,Ss).
Your second example query yields the desired result
?- listas_unitarias([[1,2],[1,2,3],[3,4,5]],U).
U = []
For your first example query the result is slightly different:
?- listas_unitarias([[1,2],[1,2,3],[3]], U).
U = [[3]] ? ;
no
The only unitary list is in a list itself. That would make more sense, since the first argument might contain more than one such list. Consider the following case:
?- listas_unitarias([[1],[2,3],[4],[]],U).
U = [[1],[4]] ? ;
no
However, if you meant to get the unitary lists one at a time, the predicate would look slightly different:
listas_unitarias2([L|_Ls],L) :-
length(L,1).
listas_unitarias2([_L|Ls],U) :-
listas_unitarias2(Ls,U).
As would the results of the queries:
?- listas_unitarias2([[1,2],[1,2,3],[3]], U).
U = [3] ? ;
no
?- listas_unitarias2([[1],[2,3],[4],[]],U).
U = [1] ? ;
U = [4] ? ;
no
Especially your second example query: It would fail instead of producing the empty list as a solution:
?- listas_unitarias2([[1,2],[1,2,3],[3,4,5]],U).
no
?- listas_unitarias2([[1,2],[1,2,3],[3,4,5]],[]).
no
EDIT: As pointed out by #false in the comments the combined use of length/2 and dif/2 in the third rule doesn't terminate for [_,_|_] so the query
?- listas_unitarias([[1],[_,_|_],[2],[3,4]],U).
U = [[1],[2]] ? ;
U = [[1],[2]] ? ;
...
does not terminate as well. However, it is reasonable to expect termination in this case, since a list headed by two elements certainly can't be unitary. So, instead of using length/2 you might consider describing the four cases that cover all possibilities. 1) If the first list is empty so is the second list. 2) If the head of the first list is [] it's not in the second list. 3) If the head of the first list is [A] it is in the second list. 4) If the head of the first list has at least two elements it's not in the second list.
listas_unitarias([],[]). % case 1)
listas_unitarias([[]|Ls],Ss) :- % case 2)
listas_unitarias(Ls,Ss).
listas_unitarias([[A]|Ls],[[A]|Ss]) :- % case 3)
listas_unitarias(Ls,Ss).
listas_unitarias([[_,_|_]|Ls],Ss) :- % case 4)
listas_unitarias(Ls,Ss).
With this version the above query terminates after finding the only solution:
?- listas_unitarias([[1],[_,_|_],[2],[3,4]],U).
U = [[1],[2]]
The other queries from above yield the same results:
?- listas_unitarias([[1,2],[1,2,3],[3,4,5]],U).
U = []
?- listas_unitarias([[1,2],[1,2,3],[3]], U).
U = [[3]]
?- listas_unitarias([[1],[2,3],[4],[]],S).
S = [[1],[4]]

How I get most repetitive element of this? (Prolog - Logic Paradigm)

In predicate "sample", the first element is your id and the others, are the result of the sample.
sample(1,04,05,30,33,41,52).
sample(2,05,37,39,41,43,49).
sample(3,05,11,29,30,36,47).
sample(4,01,05,06,27,42,59).
I need to get the most repeated value of sample... In this case is the "05" and I don't have idea how I do this. How I can do this?
If the id's are consecutive and start with 1, then this is a possible structure for a solution:
I would first change the format of the represented samples:
:- dynamic([sample/2,sample/7]).
represent_samples:-
retract(sample(I,S1,S2,S3,S4,S5,S6)),
assert(sample(I,[S1,S2,S3,S4,S5,S6])),
fail.
represent_samples.
The first line :- dynamic([sample/2,sample/7]). declares the predicates sample/2 and sample/7 to be dynamic predicates; that means that clauses of them can be added and retracted from your knowledge base. The retract line retracts a sample/7 fact from the knowlege, while the next line starting with assert adds it sample/2 representation. The line fail ensures that this is done as long as there are sample/7 facts in the knowledge base and the single represent_samples. fact guarantees that your predicate represent_samples/0 succeeds.
You can check the effect of this predicate by the following sequence of queries:
?- listing([sample/2,sample/7]).
?- represent_samples.
?- listing([sample/2,sample/7]).
After re-representing the samples one can go through them (guided by the id).
most_rep(N):-
most_rep(1,[],N).
most_rep(I,Acc,N):-
sample(I,L),
append(L,Acc,NewAcc),
I1 is I+1,
most_rep(I1,NewAcc,N).
most_rep(_I,Acc,N):-
choose_most_freq(Acc,N).
Now we only have to write the predicate choose_most_freq/2 that identifies the most frequent element in a list.
For this problem you could use the solution posted on prolog: maximally repeated element in a list
choose_most_freq(L, M) :-
setof(I-E, C^(aggregate(count, member(E, L), C), I is -C), [_-M|_]).

Prolog - Using Bagof

I've been stuck on a past paper question while studying for my exams.
The question is:
https://gyazo.com/ee2fcd88d67068e8cf7d478a98f486a0
I figured I've got to use findall/bagof/setof because I need to collect a set of solutions. Furthermore, setof seems appropriate because the list needs to be presented in descending order.
My solution so far is:
teams(List) :-
setof((Team, A),
(Team^team(Team, _, Wins, Draws, _), A is Wins*3 + Draws*1),
List).
However the problem is I don't quite get the answers all in one list. I'm very likely using Team^ incorrectly. I'd really appreciate pointers on how I can get a list of ordered tuples in terms of points. The output it gives me is:
X = [(queenspark,43)] ? ;
X = [(stirling,26)] ? ;
X = [(clyde,25)] ? ;
X = [(peterhead,35)] ? ;
X = [(rangers,63)] ? ;
Also, it's not really apparent what kind of order, if any it's in, so I'm also lost as to how setof is ordering.
Whats the best way to approach this question using setof?
Thanks.
Firstly, I would suggest to change (Team,A) to a pair representation A-Team with the A being in front since this is the total score of the team and thus the key you want to use for sorting. Then you would like to prefix the variables that should not be in the list with a ^ in front of the query you want to aggregate. See the following example:
?- setof(A-Team, P^Wins^Draws^L^(team(Team, P, Wins, Draws, L), A is Wins*3 + Draws*1), List).
List = [25-clyde,26-stirling,35-peterhead,43-queenspark,63-rangers]
Since you asked, consider the following query with the pair ordering flipped to Team-A for comparison reasons:
?- setof(Team-A,P^Wins^Draws^L^(team(Team,P,Wins,Draws,L), A is Wins*3 + Draws*1),List).
List = [clyde-25,peterhead-35,queenspark-43,rangers-63,stirling-26]
Now the resulting list is sorted with respect to the teamnames. So A-Team is the opportune choice. You could then use the predicate lists:reverse/2 to reverse the order to a descending list and then define an auxilary predicate pair_second/2 that you can use with apply:maplist/3 to get rid of the leading scores in the pairs:
:- use_module(library(lists)).
:- use_module(library(apply)).
% team(+Name, +Played, +Won, +Drawn, +Lost)
team(clyde,26,7,4,15).
team(peterhead,26,9,8,9).
team(queenspark,24,12,7,5).
team(rangers,26,19,6,1).
team(stirling,25,7,5,13).
pair_second(A-B,B). % 2nd argument is 2nd element of pair
teams(Results) :-
setof(A-Team,
P^Wins^Draws^L^(team(Team, P, Wins, Draws, L), A is Wins*3 + Draws*1),
List),
reverse(List,RList),
maplist(pair_second,RList,Results). % apply pair_second/2 to RList
If you query the predicate now you get the desired results:
?- teams(T).
T = [rangers,queenspark,peterhead,stirling,clyde]
Concerning your question in the comments: Yes, of course that is possible. You can write a predicate that describes a relation between a list of pairs and a list than only consists of the second element of the pairs. Let's call it pairlist_namelist/2:
pairlist_namelist([],[]).
pairlist_namelist([S-N|SNs],[N|Ns]) :-
pairlist_namelist(SNs,Ns).
Then you can define teams/1 like so:
teams(Results) :-
setof(A-Team,
P^Wins^Draws^L^(team(Team, P, Wins, Draws, L), A is Wins*3 + Draws*1),
List),
reverse(List,RList),
pairlist_namelist(RList,Results).
In this case, besides maplist/3, you don't need pair_second/2 either. Also you don't need to include :- use_module(library(apply)). The example query above yields the same result with this version.

Counter-intuitive behavior of min_member/2

min_member(-Min, +List)
True when Min is the smallest member in the standard order of terms. Fails if List is empty.
?- min_member(3, [1,2,X]).
X = 3.
The explanation is of course that variables come before all other terms in the standard order of terms, and unification is used. However, the reported solution feels somehow wrong.
How can it be justified? How should I interpret this solution?
EDIT:
One way to prevent min_member/2 from succeeding with this solution is to change the standard library (SWI-Prolog) implementation as follows:
xmin_member(Min, [H|T]) :-
xmin_member_(T, H, Min).
xmin_member_([], Min0, Min) :-
( var(Min0), nonvar(Min)
-> fail
; Min = Min0
).
xmin_member_([H|T], Min0, Min) :-
( H #>= Min0
-> xmin_member_(T, Min0, Min)
; xmin_member_(T, H, Min)
).
The rationale behind failing instead of throwing an instantiation error (what #mat suggests in his answer, if I understood correctly) is that this is a clear question:
"Is 3 the minimum member of [1,2,X], when X is a free variable?"
and the answer to this is (to me at least) a clear "No", rather than "I can't really tell."
This is the same class of behavior as sort/2:
?- sort([A,B,C], [3,1,2]).
A = 3,
B = 1,
C = 2.
And the same tricks apply:
?- min_member(3, [1,2,A,B]).
A = 3.
?- var(B), min_member(3, [1,2,A,B]).
B = 3.
The actual source of confusion is a common problem with general Prolog code. There is no clean, generally accepted classification of the kind of purity or impurity of a Prolog predicate. In a manual, and similarly in the standard, pure and impure built-ins are happily mixed together. For this reason, things are often confused, and talking about what should be the case and what not, often leads to unfruitful discussions.
How can it be justified? How should I interpret this solution?
First, look at the "mode declaration" or "mode indicator":
min_member(-Min, +List)
In the SWI documentation, this describes the way how a programmer shall use this predicate. Thus, the first argument should be uninstantiated (and probably also unaliased within the goal), the second argument should be instantiated to a list of some sort. For all other uses you are on your own. The system assumes that you are able to check that for yourself. Are you really able to do so? I, for my part, have quite some difficulties with this. ISO has a different system which also originates in DEC10.
Further, the implementation tries to be "reasonable" for unspecified cases. In particular, it tries to be steadfast in the first argument. So the minimum is first computed independently of the value of Min. Then, the resulting value is unified with Min. This robustness against misuses comes often at a price. In this case, min_member/2 always has to visit the entire list. No matter if this is useful or not. Consider
?- length(L, 1000000), maplist(=(1),L), min_member(2, L).
Clearly, 2 is not the minimum of L. This could be detected by considering the first element of the list only. Due to the generality of the definition, the entire list has to be visited.
This way of handling output unification is similarly handled in the standard. You can spot those cases when the (otherwise) declarative description (which is the first of a built-in) explicitly refers to unification, like
8.5.4 copy_term/2
8.5.4.1 Description
copy_term(Term_1, Term_2) is true iff Term_2 unifies
with a term T which is a renamed copy (7.1.6.2) of
Term_1.
or
8.4.3 sort/2
8.4.3.1 Description
sort(List, Sorted) is true iff Sorted unifies with
the sorted list of List (7.1.6.5).
Here are those arguments (in brackets) of built-ins that can only be understood as being output arguments. Note that there are many more which effectively are output arguments, but that do not need the process of unification after some operation. Think of 8.5.2 arg/3 (3) or 8.2.1 (=)/2 (2) or (1).
8.5.4 1 copy_term/2 (2),
8.4.2 compare/3 (1),
8.4.3 sort/2 (2),
8.4.4 keysort/2 (2),
8.10.1 findall/3 (3),
8.10.2 bagof/3 (3),
8.10.3 setof/3 (3).
So much for your direct questions, there are some more fundamental problems behind:
Term order
Historically, "standard" term order1 has been defined to permit the definition of setof/3 and sort/2 about 1982. (Prior to it, as in 1978, it was not mentioned in the DEC10 manual user's guide.)
From 1982 on, term order was frequently (erm, ab-) used to implement other orders, particularly, because DEC10 did not offer higher-order predicates directly. call/N was to be invented two years later (1984) ; but needed some more decades to be generally accepted. It is for this reason that Prolog programmers have a somewhat nonchalant attitude towards sorting. Often they intend to sort terms of a certain kind, but prefer to use sort/2 for this purpose — without any additional error checking. A further reason for this was the excellent performance of sort/2 beating various "efficient" libraries in other programming languages decades later (I believe STL had a bug to this end, too). Also the complete magic in the code - I remember one variable was named Omniumgatherum - did not invite copying and modifying the code.
Term order has two problems: variables (which can be further instantiated to invalidate the current ordering) and infinite terms. Both are handled in current implementations without producing an error, but with still undefined results. Yet, programmers assume that everything will work out. Ideally, there would be comparison predicates that produce
instantiation errors for unclear cases like this suggestion. And another error for incomparable infinite terms.
Both SICStus and SWI have min_member/2, but only SICStus has min_member/3 with an additional argument to specify the order employed. So the goal
?- min_member(=<, M, Ms).
behaves more to your expectations, but only for numbers (plus arithmetic expressions).
Footnotes:
1 I quote standard, in standard term order, for this notion existed since about 1982 whereas the standard was published 1995.
Clearly min_member/2 is not a true relation:
?- min_member(X, [X,0]), X = 1.
X = 1.
yet, after simply exchanging the two goals by (highly desirable) commutativity of conjunction, we get:
?- X = 1, min_member(X, [X,0]).
false.
This is clearly quite bad, as you correctly observe.
Constraints are a declarative solution for such problems. In the case of integers, finite domain constraints are a completely declarative solution for such problems.
Without constraints, it is best to throw an instantiation error when we know too little to give a sound answer.
This is a common property of many (all?) predicates that depend on the standard order of terms, while the order between two terms can change after unification. Baseline is the conjunction below, which cannot be reverted either:
?- X #< 2, X = 3.
X = 3.
Most predicates using a -Value annotation for an argument say that pred(Value) is the same
as pred(Var), Value = Var. Here is another example:
?- sort([2,X], [3,2]).
X = 3.
These predicates only represent clean relations if the input is ground. It is too much to demand the input to be ground though because they can be meaningfully used with variables, as long as the user is aware that s/he should not further instantiate any of the ordered terms. In that sense, I disagree with #mat. I do agree that constraints can surely make some of these relations sound.
This is how min_member/2 is implemented:
min_member(Min, [H|T]) :-
min_member_(T, H, Min).
min_member_([], Min, Min).
min_member_([H|T], Min0, Min) :-
( H #>= Min0
-> min_member_(T, Min0, Min)
; min_member_(T, H, Min)
).
So it seems that min_member/2 actually tries to unify Min (the first argument) with the smallest element in List in the standard order of terms.
I hope I am not off-topic with this third answer. I did not edit one of the previous two as I think it's a totally different idea. I was wondering if this undesired behaviour:
?- min_member(X, [A, B]), A = 3, B = 2.
X = A, A = 3,
B = 2.
can be avoided if some conditions can be postponed for the moment when A and B get instantiated.
promise_relation(Rel_2, X, Y):-
call(Rel_2, X, Y),
when(ground(X), call(Rel_2, X, Y)),
when(ground(Y), call(Rel_2, X, Y)).
min_member_1(Min, Lst):-
member(Min, Lst),
maplist(promise_relation(#=<, Min), Lst).
What I want from min_member_1(?Min, ?Lst) is to expresses a relation that says Min will always be lower (in the standard order of terms) than any of the elements in Lst.
?- min_member_1(X, L), L = [_,2,3,4], X = 1.
X = 1,
L = [1, 2, 3, 4] .
If variables get instantiated at a later time, the order in which they get bound becomes important as a comparison between a free variable and an instantiated one might be made.
?- min_member_1(X, [A,B,C]), B is 3, C is 4, A is 1.
X = A, A = 1,
B = 3,
C = 4 ;
false.
?- min_member_1(X, [A,B,C]), A is 1, B is 3, C is 4.
false.
But this can be avoided by unifying all of them in the same goal:
?- min_member_1(X, [A,B,C]), [A, B, C] = [1, 3, 4].
X = A, A = 1,
B = 3,
C = 4 ;
false.
Versions
If the comparisons are intended only for instantiated variables, promise_relation/3 can be changed to check the relation only when both variables get instantiated:
promise_relation(Rel_2, X, Y):-
when((ground(X), ground(Y)), call(Rel_2, X, Y)).
A simple test:
?- L = [_, _, _, _], min_member_1(X, L), L = [3,4,1,2].
L = [3, 4, 1, 2],
X = 1 ;
false.
! Edits were made to improve the initial post thanks to false's comments and suggestions.
I have an observation regarding your xmin_member implementation. It fails on this query:
?- xmin_member(1, [X, 2, 3]).
false.
I tried to include the case when the list might include free variables. So, I came up with this:
ymin_member(Min, Lst):-
member(Min, Lst),
maplist(#=<(Min), Lst).
Of course it's worse in terms of efficiency, but it works on that case:
?- ymin_member(1, [X, 2, 3]).
X = 1 ;
false.
?- ymin_member(X, [X, 2, 3]).
true ;
X = 2 ;
false.

Resources