Prolog: Random Labeling - prolog

I have a program written in Sicstus Prolog using constraints.
My goal is to use labeling/2 and some other method to obtain a random instantiation of my variables.
Example:
X #> 2, Y #= 2*X, Z #<10
If I use
List = [X,Y,Z],
labeling([], List)
The first result obtained will be X = Y = Z = 0. How do you think is the best way to return a random set of values for X, Y and Z?

I do not know much about the labeling options in recent SICStus versions, but with library(clpfd) of SWI-Prolog, there are the options random_variable(Seed) and random_value(Seed), you can use them for example with labeling([random_variable(10),random_value(10)], List). Maybe you can get the authors of SICStus to integrate similar options?

In sicstus, this is done with a custom selection of variables / values.
In your case, just do:
labeling([value(mySelValores)], List)
mySelValores(Var, _Rest, BB, BB1) :-
fd_set(Var, Set),
select_best_value(Set, Value),
(
first_bound(BB, BB1), Var #= Value
;
later_bound(BB, BB1), Var #\= Value
).
select_best_value(Set, BestValue):-
fdset_to_list(Set, Lista),
length(Lista, Len),
random(0, Len, RandomIndex),
nth0(RandomIndex, Lista, BestValue).
See value(Enum) in https://sicstus.sics.se/sicstus/docs/4.0.4/html/sicstus/Enumeration-Predicates.html.
Hope it helps ;)

you can use
all_different([X,Y,Z]) in order to get different values
however, working with random seed in Sicstus could be tricky and you may need to define a function to change the seed or start the random function again.
check below
www.sics.se/sicstus/docs/3.7.1/html/sicstus_23.html

I have opted in Jekejeke Prolog, for a new predicate random_labeling/1 in connection
with CLP(FD) that takes implicitly the random number generator from the knowledge base
that can be accessed and modified via the sys_random Prolog flag.
Jekejeke Prolog 3, Runtime Library 1.3.4
(c) 1985-2019, XLOG Technologies GmbH, Switzerland
?- use_module(library(finite/clpfd)).
% 20 consults and 0 unloads in 944 ms.
Yes
?- use_module(library(basic/random)).
% 0 consults and 0 unloads in 0 ms.
Yes
?- random_new(111,R), set_prolog_flag(sys_random,R),
X in 0..5, Y #= X*X, random_label([X,Y]),
write(X-Y), nl, fail; true.
4-16
3-9
5-25
1-1
2-4
0-0
Yes
?- random_new(111,R), set_prolog_flag(sys_random,R),
X in 0..5, Y #= X*X, random_label([X,Y]),
write(X-Y), nl, fail; true.
4-16
3-9
5-25
1-1
2-4
0-0
I am planning a further predicate random_labeling/2. But it would not take a seed, but instead a Java java.util.Random instance. This is more versatile than a seed. But I guess changing the API to labeling/2 and some options would be the best way to go.
Edit 29.12.2018: I will now take notes, since I guess its a good idea
to adopt indomain/2, currently I have implemented random_indomain/1,
and from this implemented random_label/1. See also here:
indomain/2 from ECLiPSe Prolog
random: Try the enumeration in a random order. On backtracking,
the previously tested value is removed. This method uses random/1 to
create random numbers, use seed/1 before to make results reproducible.
http://eclipseclp.org/doc/bips/lib/gfd_search/indomain-2.html

Related

How to count the predicates

I'm looking for a way to count the numbers of predicates.
Example:
%facts
has_subclass(thing,animal).
has_subclass(thing,tree).
has_subclass(thing,object).
% and I ask
count_has_subclass(thing,X).
% result
X = 3.
For facts like your example:
count_has_subclass(What, Count):-
findall(1, call(has_subclass(What, _)), L),
length(L, Count).
We can use findall/3 for this, and then use length/2 to obtain the length of the list:
count_has_subclass(What, N):-
findall(X, has_subclass(What, X), L),
length(L, N).
If it is however possible that has_subclass/2 yields the same values multiple times for a given key (like thing), then we can use for example sort/2 as a duplicate filter, like:
count_has_subclass(What, N):-
findall(X, has_subclass(What, X), L),
sort(L, S), %% remove duplicates
length(S, N).
Note that if What is a free variable, then you will count all yeilds of has_subclass(_, _). (optionally with a uniqness filter on the second parameter).
Using the standard setof/3 is a better option as it allows easy definition of a more general predicate that can enumerate solutions when the class argument is not bound. For example, assume the following database:
has_subclass(thing,animal).
has_subclass(thing,tree).
has_subclass(thing,object).
has_subclass(animal,cat).
has_subclass(animal,dog).
has_subclass(tree,pine).
has_subclass(tree,oak).
And the definition:
subclass_count(Class, Count) :-
setof(Subclass, has_subclass(Class, Subclass), Subclasses),
length(Subclasses, Count).
Sample call:
| ?- subclass_count(Class, Count).
Class = animal
Count = 2 ? ;
Class = thing
Count = 3 ? ;
Class = tree
Count = 2
yes
If you try instead one of the findall/3 solutions in the other answers, we get instead:
| ?- count_has_subclass(What, Count).
Count = 7
But note that this solution also have a sensible interpretation as returning the number of all existing subclasses when the class is not specified.
To build a list just to count solutions seems 'old style', but in traditional Prolog there is only the DB (assert/retract) alternative to overcome the 'stateless' nature of computations. Indeed findall/3 and friends builtins could be (naively) rewritten by means of assert/retract. But something have shown up since the early 80's ages :). I illustrate with SWI-Prolog, but - more or less naively - all of them could be implemented by assert/retract. Of course, most Prolog implementations have 'non logical' (or 'imperative', or 'impure') facilities to implement such basic task, without resorting to the heavyweight DB interface - like setarg/3, nb_setval/2 and others...
I digress... library(aggregate) should be shown first:
?- aggregate(count, T^has_subclass(thing,T), C).
C = 3.
This lib is well worth to study, does a lot more than - efficiently - counting...
Another, recent, addition is library(solution_sequences). It's not more efficient than setof/3 + length/2, guess, but interesting in its own. Counting is a bit more involved, and uses call_nth/2:
?- order_by([desc(C)],call_nth(has_subclass(thing,T),C)).
C = 3,
T = object ;
...
Some far simpler code of mine, based on nb_setarg/3 (and help from #false):
?- [carlo(snippets/lag)].
true.
?- integrate(count,has_subclass(thing,T),C).
C = 3.

Get a value from residual goals

If I have X #> 3. Prolog will give the residual goal X in 4..sup.. How can I assign X one of the possible values from 4 to sup? Any random value will suffice.
In SWI-Prolog,you can get random value assign with using secret option random_value(Seed).
see this:How does `random_variable `random_value` work in SWI-Prolog's labeling/2?
:- use_module(library(clpfd)).
random_labeling_test(A):-
A in 1..5,
labeling([random_value(332)],[A]).
?- random_labeling_test(A).
A = 5 ;
A = 1 ;
A = 2 ;
A = 4 ;
A = 3.
332 is meaningless.Set random seed into here.current milliseconds is often used .
I'm not sure this option is safe.
But in your case,this cannot work because upper bounds is infinite.It is reasonable.
In ECLiPSe, labeling variable selection and assigning value selection is completly controllable with using indomain and delete.
see this:http://eclipseclp.org/doc/tutorial/tutorial088.html
One naive way to solve this is to use a predicate like this:
enumeration(Z) :-
length(_, N),
enumeration_(N, Z).
enumeration_(N, N).
enumeration_(N0, N) :- N #= -N0.
You can now solve your task as follows:
?- X #> 3, enumeration(X).
X = 4 ;
X = 5 ;
X = 6 ;
X = 7 ;
X = 8 .
Pros:
a quite simple solution
portable to different Prolog systems.
Con:
potentially quite slow.
Example:
?- X #> 2^100, enumeration(X).
[waiting...]
To make this more efficient, you need to take into account the actual domains of variables.
There are at least two ways to do this:
(a) Use your solver's reflection predicates
Reflection predicates let you reason about the actual domains of variables by making them available as Prolog terms.
For example, where available, you can use fd_inf/2 to obtain the infimum of a constrained variable's domain:
?- X #> 3, fd_inf(X, Inf).
Inf = 4,
X in 4..sup.
You can use this as a starting point for the enumeration. I leave this as a challenge.
See your Prolog system's manual for more information about its reflection predicates.
(b) Inspect the residual goals
Alternatively, you can inspect the residual goals as Prolog terms. This is also a reflection mechanism, though one that only needs a single predicate for all kinds of different constraint solvers.
For example, where available, you can use copy_term/3 to obtain the residual goals as a list of Prolog goals:
?- X #> 3, copy_term(X, X, Gs).
Gs = [clpfd:(X in 4..sup)],
X in 4..sup.
From these goals, it is again straight-forward to deduce the infimum of X's domain.
Note that the top-level can use this same mechanism to actually produce the residual goals.

Setting types of unbound variables in Prolog

I'm trying to find a way to set the type of a variable before it has been bound to a value. Unfortunately, the integer/1 predicate cannot be used for this purpose:
%This goal fails if Int is an unbound variable.
get_first_int(Int,List) :-
integer(Int),member(Int,List),writeln(Int).
I wrote a predicate called is_int that attempts to check the type in advance, but it does not work as I expected. It allows the variable to be bound to an atom instead of an integer:
:- initialization(main).
%This prints 'a' instead of 1.
main :- get_first_int(Int,[a,b,c,1]),writeln(Int).
get_first_int(Int,List) :-
is_integer(Int),member(Int,List).
is_integer(A) :- integer(A);var(A).
Is it still possible to set the type of a variable that is not yet bound to a value?
In SWI-Prolog I have used when/2 for similar situations. I really don't know if it is a good idea, it definitely feels like a hack, but I guess it is good enough if you just want to say "this variable can only become X" where X is integer, or number, or atom and so on.
So:
will_be_integer(X) :- when(nonvar(X), integer(X)).
and then:
?- will_be_integer(X), member(X, [a,b,c,1]).
X = 1.
But I have the feeling that almost always you can figure out a less hacky way to achieve the same. For example, why not just write:
?- member(X, [a,b,c,1]), integer(X).
???
Specific constraints for integers
In addition to what Boris said, I have a recommendation for the particular case of integers: Consider using CLP(FD) constraints to express that a variable must be of type integer. To express only this quite general requirement, you can post a CLP(FD) constraint that necessarily holds for all integers.
For example:
?- X in inf..sup.
X in inf..sup.
From this point onwards, X can only be instantiated to an integer. Everything else will yield a type error.
For example:
?- X in inf..sup, X = 3.
X = 3.
?- X in inf..sup, X = a.
ERROR: Type error: `integer' expected, found `a' (an atom)
Declaratively, you can always replace a type error with silent failure, since no possible additional instantiation can make the program succeed if this error arises.
Thus, in case you prefer silent failure over this type error, you can obtain it with catch/3:
?- X in inf..sup, catch(X = a, error(type_error(integer,_),_), false).
false.
CLP(FD) constraints are tailor-made for integers, and let you express also further requirements for this specific domain in a convenient way.
Case-specific advice
Let us consider your specific example of get_first_int/2. First, let us rename it to list_first_integer/3 so that it is clear what each argument is, and also to indicate that we fully intend to use it in several directions, not just to "get", but also to test and ideally to generate lists and integers that are in this relation.
Second, note that this predicate is rather messy, since it impurely depends on the instantiation of the list and integer, a property which cannot be expressed in first-order logic but rather depends on something outside of this logic. If we accept this, then one quite straight-forward way to do what you primarily want is to write it as:
list_first_integer(Ls, I) :-
once((member(I0, Ls), integer(I0))),
I = I0.
This works as long as the list is sufficiently instantiated, which implicitly seems to be the case in your examples, but definitely need not be the case in general. For example, with fully instantiated lists, we get:
?- list_first_integer([a,b,c], I).
false.
?- list_first_integer([a,b,c,4], I).
I = 4.
?- list_first_integer([a,b,c,4], 3).
false.
In contrast, if the list is not sufficiently instantiated, then we have the following major problems:
?- list_first_integer(Ls, I).
nontermination
and further:
?- list_first_integer([X,Y,Z], I).
false.
even though a more specific instantiation succeeds:
?- X = 0, list_first_integer([X,Y,Z], I).
X = I, I = 0.
Core problem: Defaulty representation
The core problem is that you are reasoning here about defaulty terms: A list element that is still a variable may either be instantiated to an integer or to any other term in the future. A clean way out is to design your data representation to symbolically distinguish the possible cases. For example, let us use the wrapper i/1 to denote an integer, and o/1 to denote any other kind of term. With this representation, we can write:
list_first_integer([i(I)|_], I).
list_first_integer([o(_)|Ls], I) :-
list_first_integer(Ls, I).
Now, we get correct results:
?- list_first_integer([X,Y,Z], I).
X = i(I) ;
X = o(_12702),
Y = i(I) ;
X = o(_12702),
Y = o(_12706),
Z = i(I) ;
false.
?- X = i(0), list_first_integer([X,Y,Z], I).
X = i(0),
I = 0 ;
false.
And the other examples also still work, if we only use the clean data representation:
?- list_first_integer([o(a),o(b),o(c)], I).
false.
?- list_first_integer([o(a),o(b),o(c),i(4)], I).
I = 4 ;
false.
?- list_first_integer([o(a),o(b),o(c),i(4)], 3).
false.
The most general query now allows us to generate solutions:
?- list_first_integer(Ls, I).
Ls = [i(I)|_16880] ;
Ls = [o(_16884), i(I)|_16890] ;
Ls = [o(_16884), o(_16894), i(I)|_16900] ;
Ls = [o(_16884), o(_16894), o(_16904), i(I)|_16910] ;
etc.
The price you have to pay for this generality lies in these symbolic wrappers. As you seem to care about correctness and also about generality of your code, I consider this a bargain in comparison to more error prone defaulty approaches.
Synthesis
Note that CLP(FD) constraints can be naturally used together with a clean representation. For example, to benefit from more finely grained type errors as explained above, you can write:
list_first_integer([i(I)|_], I) :- I in inf..sup.
list_first_integer([o(_)|Ls], I) :-
list_first_integer(Ls, I).
Now, you get:
?- list_first_integer([i(a)], I).
ERROR: Type error: `integer' expected, found `a' (an atom)
Initially, you may be faced with a defaulty representation. In my experience, a good approach is to convert it to a clean representation as soon as you can, for the sake of the remainder of your program in which you can then distinguish all cases symbolically in such a way that no ambiguity remains.

Steadfastness: Definition and its relation to logical purity and termination

So far, I have always taken steadfastness in Prolog programs to mean:
If, for a query Q, there is a subterm S, such that there is a term T that makes ?- S=T, Q. succeed although ?- Q, S=T. fails, then one of the predicates invoked by Q is not steadfast.
Intuitively, I thus took steadfastness to mean that we cannot use instantiations to "trick" a predicate into giving solutions that are otherwise not only never given, but rejected. Note the difference for nonterminating programs!
In particular, at least to me, logical-purity always implied steadfastness.
Example. To better understand the notion of steadfastness, consider an almost classical counterexample of this property that is frequently cited when introducing advanced students to operational aspects of Prolog, using a wrong definition of a relation between two integers and their maximum:
integer_integer_maximum(X, Y, Y) :-
Y >= X,
!.
integer_integer_maximum(X, _, X).
A glaring mistake in this—shall we say "wavering"—definition is, of course, that the following query incorrectly succeeds:
?- M = 0, integer_integer_maximum(0, 1, M).
M = 0. % wrong!
whereas exchanging the goals yields the correct answer:
?- integer_integer_maximum(0, 1, M), M = 0.
false.
A good solution of this problem is to rely on pure methods to describe the relation, using for example:
integer_integer_maximum(X, Y, M) :-
M #= max(X, Y).
This works correctly in both cases, and can even be used in more situations:
?- integer_integer_maximum(0, 1, M), M = 0.
false.
?- M = 0, integer_integer_maximum(0, 1, M).
false.
| ?- X in 0..2, Y in 3..4, integer_integer_maximum(X, Y, M).
X in 0..2,
Y in 3..4,
M in 3..4 ? ;
no
Now the paper Coding Guidelines for Prolog by Covington et al., co-authored by the very inventor of the notion, Richard O'Keefe, contains the following section:
5.1 Predicates must be steadfast.
Any decent predicate must be “steadfast,” i.e., must work correctly if its output variable already happens to be instantiated to the output value (O’Keefe 1990).
That is,
?- foo(X), X = x.
and
?- foo(x).
must succeed under exactly the same conditions and have the same side effects.
Failure to do so is only tolerable for auxiliary predicates whose call patterns are
strongly constrained by the main predicates.
Thus, the definition given in the cited paper is considerably stricter than what I stated above.
For example, consider the pure Prolog program:
nat(s(X)) :- nat(X).
nat(0).
Now we are in the following situation:
?- nat(0).
true.
?- nat(X), X = 0.
nontermination
This clearly violates the property of succeeding under exactly the same conditions, because one of the queries no longer succeeds at all.
Hence my question: Should we call the above program not steadfast? Please justify your answer with an explanation of the intention behind steadfastness and its definition in the available literature, its relation to logical-purity as well as relevant termination notions.
In 'The craft of prolog' page 96 Richard O'Keef says 'we call the property of refusing to give wrong answers even when the query has an unexpected form (typically supplying values for what we normally think of as inputs*) steadfastness'
*I am not sure if this should be outputs. i.e. in your query ?- M = 0, integer_integer_maximum(0, 1, M). M = 0. % wrong! M is used as an input but the clause has been designed for it to be an output.
In nat(X), X = 0. we are using X as an output variable not an input variable, but it has not given a wrong answer, as it does not give any answer. So I think under that definition it could be steadfast.
A rule of thumb he gives is 'postpone output unification until after the cut.' Here we have not got a cut, but we still want to postpone the unification.
However I would of thought it would be sensible to have the base case first rather than the recursive case, so that nat(X), X = 0. would initially succeed .. but you would still have other problems..

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