1

Given this small example:

go:-
  length( X, 200 ),
  domain( X, 1, 25),

  postConstraints( X, Y ),

  labeling( [minimize(Y), X ).

If we assume that postConstraints set up some complex constraints. Y is returned from postConstraints and used as cost-function during labeling.

We assume that we have no (or minimal) knowledge about the constraints set up by postConstraints. But we know that the optimal solution (or a good solution) will be that X contains a more or less uniform distribution of the possible domain. I.e. the value 1 will appear around 8 (200/25) times, 2 will appear around 8 times, etc.

However we don't know at what position each value will appear.

If we start by use of default labeling, X will first be assigned with only 1, which is a solution, but not a good solution (high Y). By running the search for a long time, the optimal solution will be found, which is a more or less uniform distribution over the possible domain.

This mean that the search use a long time to go from the first possible solution to the optimal (or the better) solution.

I think that if an initial 'guess' could be applied to X before labeling, the search would be faster. Ex. if X was populated with random values from the domain? Is there any way to do that in Sicstus? Is this where you use value(Enum) in labeling?

MortenM
  • 522
  • 2
  • 12

2 Answers2

1

Your question does not contain a concrete example, so it is difficult to give concrete advice. However, you might consider the labeling option ff first. At least for a simple reason: Adding a predefined labeling option might influence runtime but not correctness. More complex approaches always risk that you introduce errors.

The predicate labeling/2 offers two predefined ways to enumerate the values of a selected variable: up (default) and down. To start at a different value you may map a variable to a different one that again uses one of the built-in enumerations. Defining your own enumeration method is possible, but definitely not a task for beginners. In fact, even library/clpfd/examples/ does not provide a single example.

To illustrate how variables can be enumerated differently, I will use a single variable X:

?- X in 1..5, labeling([],[X]).
   X = 1
;  X = 2
;  X = 3
;  X = 4
;  X = 5.
?- X in 1..5, labeling([down],[X]).
   X = 5
;  X = 4
;  X = 3
;  X = 2
;  X = 1.

Now we want to startX with the value 3. Thus X is mapped to Xx which will be used in labeling instead:

?- X in 1..5, Xx #= (X+5-3)mod 5,labeling([],[Xx]).
   X = 3, Xx = 0
;  X = 4, Xx = 1
;  X = 5, Xx = 2
;  X = 1, Xx = 3
;  X = 2, Xx = 4.

In this manner you could map each variable to some other initial value. Or all to the same. Note, however, that due to the relatively weak consistency of (mod)/2, not all information present in the original variable can be seen immediately. This in turn might deteriorate labeling, should you use an option like ff which examines the domains dynamically:

?- assert(clpfd:full_answer).
?- X in 1..5, Xx #= (X+5-3)mod 5, X #\= 2.
   clpfd:(_A#=X+2), clpfd:(_A mod 5#=Xx),
   X in{1}\/(3..5), _A in{3}\/(5..7), Xx in 0..4.

So here, the domain of Xx is not yet updated to 0..3, although:

?- X in 1..5, Xx #= (X+5-3)mod 5, X #\= 2, Xx = 4.
   false.

Also the very smart default option step is affected similarly.

false
  • 10,264
  • 13
  • 101
  • 209
0

You say the good solutions will have "more or less uniform distribution over the possible domain", which I suppose means that all domain values will occur a similar number of times in the solution vector.

In such a situation, you can introduce an auxiliary variable that somehow measures the heuristic quality of your variable assignment. You then start your search by labeling this auxiliary variable first, the other variables later. That way, the potentially good assignments can be examined first.

I don't have SICStus, but here is an ECLiPSe example, which should be straightforward to adapt. I have introduced constraints to count how often the different domain values 1 to ND occur in the main variable vector Xs. The imbalance of the counting variables Ns is then computed in the variable Imbalance, and that is the variable that gets labeled first (starting with the value 0, i.e. perfect balance):

:- lib(ic).
:- lib(ic_global).

main(NX, ND, Xs) :-
    length(Xs,NX),
    Xs::1..ND,
    ( for(I,1,ND), foreach(N,Ns), param(Xs) do
        occurrences(I, Xs, N)    % SICStus: count(I,Xs,#=,N)
    ),
    sum(Ns) #= NX,
    Imbalance #= max(Ns) - min(Ns),
    Imbalance :: 0..NX,

    labeling([Imbalance|Xs]).

The sample run shows how the balanced solutions are enumerated first:

?- main(4, 2, Xs), writeln(Xs), fail.
[1, 1, 2, 2]
[1, 2, 1, 2]
[1, 2, 2, 1]
[2, 1, 1, 2]
[2, 1, 2, 1]
[2, 2, 1, 1]
[1, 1, 1, 2]
[1, 1, 2, 1]
[1, 2, 1, 1]
[1, 2, 2, 2]
[2, 1, 1, 1]
[2, 1, 2, 2]
[2, 2, 1, 2]
[2, 2, 2, 1]
[1, 1, 1, 1]
[2, 2, 2, 2]
No (0.01s cpu)

Your actual problem constraints can simply be added to this template.

jschimpf
  • 4,904
  • 11
  • 24