2

I can assign a domain of possible values to a variable i.e. :

 ?- X in 1..3, X #= 3.
 X = 3.

Is there a way to add a constraint so that a variable can accept a list of values in a specified domain ? What about a constraint to a list of maximum 3 elements with values in the range 1..5 ?

And finally the last question : A variable Y that can accept a list of up to 3 elements which are in the domain of values accepted by variable X (which for the sake of it happen to be 1..5)?

Any example or hint how start to build such a constraint ?


As per lurker :

?- L=[1,2,3], length(L, 3), L ins 1..5.
L = [1, 2, 3].

?- L=[1,2,3,4], length(L, 3), L ins 1..5.
false.

?- L=[1,2,6], length(L, 3), L ins 1..5.
false.

?- L=[1,2,5], length(L, 3), L ins 1..5.
L = [1, 2, 5].


?- L=[4,5], length(L, 3), L ins 1..5.
false.

Only the last is off, should be true.. Let me refine one of the constraint :)

List with 1 to 3 elements, rather than only 3 elements.


Ha, this worked :

?- L=[4,5], X in 1..3, length(L, X), L ins 1..5.
L = [4, 5],
X = 2.

how cool is CLP :)


Now one more refinement. How to make it so that elements of the list do not repeat ?

jschimpf
  • 4,904
  • 11
  • 24
sten
  • 7,028
  • 9
  • 41
  • 63
  • 3
    You can say `length(L, 3), L ins 1..5` to get a list of length 3 where each element is in the range 1 through 5, for example. Not sure about your last question... – lurker Feb 15 '18 at 20:00
  • What version of prolog are you using? SWI, ECLiPSe? – damianodamiano Feb 15 '18 at 20:08
  • last q is restatement of second. instead of domain I'm implying find the domain from variable instead of directly defining the domain, just curious – sten Feb 15 '18 at 20:48
  • With ECLiPSe, you can assign the domain of a variable, based on a domain of another variable... – damianodamiano Feb 15 '18 at 20:54
  • 3
    If you don't want the list elements to repeat, look up the `all_distinct/1` constraint. It's in the documentation. Spoiler: `L ins 1..5, all_distinct(L)`. – lurker Feb 15 '18 at 21:06
  • i've seen it, but haven't thought of it..new to CLP. all_different/1 seem to work too. Docs say it is weaker but faster. – sten Feb 15 '18 at 21:18

3 Answers3

2

In CLP it is important to separate constraints from nondeterminism (search). In a Prolog-based framework, this distinction is not readily apparent in the language, and this often confuses beginners.

Take a predicate like Prolog's length/2: while logically this can be viewed as a "constraint" on the values of its arguments, in CLP terminology we don't usually call this a constraint, because it will nondeterministically generate values for its arguments whenever these are not sufficiently instantiated.

The behaviour we expect from a constraint is more passive: it should forbid its arguments taking values that the constraint semantics doesn't allow, but not pro-actively generate arbitrary valid values. The reason for that is that we usually want to set up a whole network of constraints (constraint setup phase) before we start exploring different variable assignments (search phase).

Now, constraint solvers come with a number of predefined constraints over a certain variable domain (the most widely used being constraints over integer variables). But you wanted to constrain a variable to take values from the domain of lists. You don't have a ready-made implementation for that, so you may want to write your own. CLP systems differ in the support they have for letting you define your own constraints. In the following examples I use ECLiPSe's delay clause feature because that's probably the most readable (Disclaimer: I'm personally involved with ECLiPSe).

Here is a simple length-constraint. Note the "delay clause" that causes the predicate to wait if either argument is uninstantiated:

delay c_len(Xs, N) if var(Xs) ; var(N).
c_len([], 0).
c_len([_|Xs], N) :-
        N1 is N-1,
        c_len(Xs, N1).

This can ensure that a variable takes only a list of a certain length:

?- c_len(Xs, 3), Xs = [_,_].
No (0.00s cpu)

?- c_len(Xs, 3), Xs = [_,_,_].
Xs = [_76, _78, _80]
Yes (0.00s cpu)

?- c_len(Xs, 3), Xs = [_,_,_,_].
No (0.00s cpu)

but when there isn't enough information, it waits (as indicated by the "delayed goal" message):

?- c_len(Xs, 3), Xs = [_,_|Tail].
Xs = [_64, _66|Tail]
There is 1 delayed goal.
Yes (0.00s cpu)

In this last example we see that, although the constraint is correct (in the sense that it doesn't allow invalid instantiations of its arguments), it could actually be bit cleverer and infer that Tail must be a list of length 1. This is fairly typical: the operational behaviour of a constraint (often referred to as its propagation strength) can vary between implementations. Usually it reflects a tradeoff between propagation strength and computational complexity.

Here is a somewhat better implementation. It takes advantage of finite domain variables, and also implements your requirement of constraining the domains of the list elements:

:- use_module(library(ic)).    % provides finite domain solver

delay bounded_list(Xs,_Lo,_Hi,Len) if var(Xs), var(Len).
bounded_list([],_Lo,_Hi, 0).
bounded_list([X|Xs], Lo, Hi, Len) :-
        Len #> 0,
        Lo #=< X, X #=< Hi,
        Len1 #= Len-1,
        bounded_list(Xs, Lo, Hi, Len1).

This can enforce your desired list properties (here length 1 to 3 and values between 1 and 5):

?- N #:: 1..3, bounded_list(Xs,1,5,N), Xs = [1,2,3].
N = 3
Xs = [1, 2, 3]
Yes (0.00s cpu)

?- N #:: 1..3, bounded_list(Xs,1,5,N), Xs = [1,2,3,4].
No (0.00s cpu)

?- N #:: 1..3, bounded_list(Xs,1,5,N), Xs = [1].
N = 1
Xs = [1]

?- N #:: 1..3, bounded_list(Xs,1,5,N), Xs = [1,9].
No (0.00s cpu)

It also does some early inferences, such as creating list elements with appropriate integer domains:

?- bounded_list(Xs, 1, 5, 3).
Xs = [_319{1..5}, _472{1..5}, _625{1..5}]
Yes (0.00s cpu, ...)
jschimpf
  • 4,904
  • 11
  • 24
1

As per lurker this seem to be a solution of a List with 1 up-to 3 elements of non repeatable numbers in the range of 1 .. 5 :

 ?- L=[4,1,Z], X in 1..3, length(L, X), L ins 1..5, all_distinct(L).
 L = [4, 1, Z],
 X = 3,
 Z in 2..3\/5,
 all_distinct([4, 1, Z]).

 ?- L=[4,1,1], X in 1..3, length(L, X), L ins 1..5, all_distinct(L).
 false.

 ?- L=[4,1,2], X in 1..3, length(L, X), L ins 1..5, all_distinct(L).
 L = [4, 1, 2],
 X = 3.

 ?- Lst=[3,1,5], Length in 1..3, length(Lst, Length), Lst ins 1..5, all_distinct(Lst).
 Lst = [3, 1, 5],
 Length = 3.
sten
  • 7,028
  • 9
  • 41
  • 63
  • When you use clp variables, the 'number' of them must be determined. So second variable of length/2 cannot be clp variable(at least makes no sense).When you apply clp constraint to the variables ,swi-prolog implicitly make them turn into clp variables(from normal prolog variables) with using attributed variable mechanism. – Taku Koyahata Feb 21 '18 at 01:14
  • When you use clpfd, you always have to beware which kind of predicate you are using , normal prolog predicates or clp predicates. Like we always beware predicate makes choice point or not. – Taku Koyahata Feb 21 '18 at 01:59
0

This code makes no sense and clpfd is not used actually.

?- L=[4,5], X in 1..3, length(L, X), L ins 1..5.

L = [4, 5],
X = 2.

after removing X in 1..3 and L ins 1..5 , it works samely.

?- L=[4,5],length(L,X).

L = [4, 5],
X = 2.
Taku Koyahata
  • 558
  • 2
  • 13
  • it is just one of the examples I gave – sten Feb 16 '18 at 05:50
  • whats not to understand : List has to be no more that 3 elements, in the specified range 1..5, no repetition. other ppl understood it !!! – sten Feb 16 '18 at 21:07