What is the uncontrollable automatic propagation behavior in ECLIPSe-CLP? - constraint-programming

I am trying to research, evaluate, and compare some searching methods in ECLIPSe-CLP. The key method of evaluating complexity and the efficacy of a method in said system is counting backtracks, implemented with the predicate search/6 from lib(ic_search). However, in my testing, I noticed that the Eclipse software applies some process similar to domain propagation automatically before entering the search, which I have no way of controlling, which apparently solves certain problems without any backtracks. This hinders my evaluation of search methods as I have no control over the domains and/or problem statement when it passes into search. I would like to know what the process employed is, and whether it can be disabled or if I can work around it. Attached below is a very primitive sample code and its tracing. Problems such as SEND + MORE = MONEY and the Australian Map coloring problems can be similarly solved with 0 backtracks.
Thanks in advance!
Eclipse code
:- lib(ic), lib(ic_global), lib(ic_search).
go :-
simpleproblem(Vars).
simpleproblem(Vars) :-
Vars = [A,B],
Vars :: [0..1],
A + B #= 2,
search(Vars,0,input_order,indomain,complete,[backtrack(Backtracks)]),
writeln(backtracks:Backtracks),
writeln(Vars).
Tracing log
(1) 1 CALL go
(2) 2 CALL simpleproblem(_598)
(3) 3 CALL ... = ...
(3) 3 EXIT ... = ...
(4) 3 CALL '::_body'([_1028, _1030], [0 .. 1], eclipse)
(4) 3 EXIT '::_body'([_1476{[0, ...]}, _1490{[...]}], [0 .. 1], eclipse)
(5) 3 CALL _1490{[0, ...]} + _1476{[0, ...]} #= 2
(9) 4 CALL wake
(6) 5 RESUME<2> 0 #= 0
(6) 5 EXIT<2> 0 #= 0
(9) 4 EXIT wake
(5) 3 EXIT 1 + 1 #= 2
(10) 3 CALL search_body([1, 1], 0, input_order, indomain, complete, [backtrack(_3046)], eclipse)

The very reason for the effectiveness of Constraint Programming is the principle of interleaving propagation with search. Because this is really the essence of CP, I am not sure it makes much sense for you to look at search methods in isolation.
Propagation is performed in a data-driven way by the implementation of each individual constraint: whenever a variable domain changes, e.g. during the initial constraint setup or by a search decision, the constraint will try to propagate the consequences. This may lead to more domain changes, which cause further propagation, and so on, until a fixpoint is reached.
Once no further propagation is possible, the search control takes over again, looks at the the constraint network and current variable domains, and decides on the next guess. When a guess is made (in the form of a variable instantiation, domain splitting, etc), propagation is triggered again.
Implementation-wise, there is a clear separation: propagation is done by the individual constraints (in your example #=/2 only), while the search control algorithm is in the search/6 predicate. However, there is a strong interdependency between them, because they communicate back-and-forth via the constraint network.
Note that most search techniques, such as the popular first-fail heuristics, heavily rely on the result of propagation, otherwise they could not make their informed guesses. While in principle it would be possible for you to use non-propagating implementations for all constraints (e.g. wait for all their variables to be instantiated, and only then test for satisfaction), this would make many search options pointless, and you would not learn much by counting backtracks.

Related

Clarification on `failures` solver statistic in MiniZinc

I have been playing around with a simple n-queens model in MiniZinc:
include "globals.mzn";
int: n_queens = 8;
array[1..n_queens] of var 1..n_queens: queens;
constraint alldifferent(queens);
constraint alldifferent(i in 1..n_queens) (queens[i] + i);
constraint alldifferent(i in 1..n_queens) (queens[i] - i);
solve satisfy;
The MiniZinc handbook mentions failures as the "number of leaf nodes that were failed". Following are the statistics after running the model:
%%%mzn-stat: initTime=0.000576
%%%mzn-stat: solveTime=0.000822
%%%mzn-stat: solutions=1
%%%mzn-stat: variables=24
%%%mzn-stat: propagators=19
%%%mzn-stat: propagations=1415
%%%mzn-stat: nodes=47
%%%mzn-stat: failures=22
%%%mzn-stat: restarts=0
%%%mzn-stat: peakDepth=5
%%%mzn-stat-end
There were 22 failures. Being a beginner to constraint programming, my understanding was that the entire purpose of the paradigm is to prune and avoid leaf nodes as much as possible. I am extra confused since the peak depth of the search tree is reported as 5 (not 8).
Am I interpreting these statistics right? If yes, why are there leaf node failures in the model? Will I create a better model by trying to reduce these failures?
Those values depend of the search strategy, some times you cannot avoid a leaf node because it hasn't been pruned, that means, nothing before it told the solver that that node was going to be a failure, modeling it in a different way can prevent some failures, and can also prevent suboptimal solutions in the case of optimization problems.
These are the first three nodes that got evaluated on the search tree of the default search strategy of minizinc, I labeled them in the image of the Search Tree in the order they got evaluated, and the 4 and 5 to show the arrival to a feasible solution.
In the the blue dots are nodes where there is still uncertainty, the red squares are failures, white dots are non evaluated nodes, large triangles are whole branches where the search only resulted in failures, the green diamond means a feasible solution, and orange diamonds mean non-best-but-feasible solution (only in optimization problems).
The explanation of each of the labeled nodes is
0: Root node: all variables are un assigned
Nothing has happened, these are all the decision variables and their full domains
queens = array1d(1..8, [[1..8], [1..8], [1..8], [1..8], [1..8], [1..8], [1..8], [1..8]]);
1: First decision
Then it picked the smallest value in the domain of the last variable and made the first split, the solver thought either queens[8] = 1(left child of the root) or queens[8] = [2..8](right child of the root), it will first evaluate queens[8] = 1 and that bring the first node to existence,
queens = array1d(1..8, [[2..7], {2..6,8}, {2..5,7..8}, {2..4,6..8}, {2..3,5..8}, {2,4..8}, [3..8], 1]);
where the decision queens[8] = 1 already propagated to the other variables and removed values from its domains.
2: The search continues
Then it again splits at queens[7], this is the left child node where queens[7] = 3, the minimum value in the domain of that variable, and the propagation of that decision to the other variables.
queens = array1d(1..8, [{2,4..7}, {2,4..6}, {2,4..5,8}, {2,4,7..8}, {2,6..8}, [5..8], 3, 1]);
In hindsight (more like cheating by looking at the image of the Search Tree) we know that this whole branch of the search will result in failures, but we cannot know that while searching, because there is still uncertainty in some variables, to know that we would have to evaluate all of the possibilities, which are possibly feasible, that might happen or not, hopefully we will find a satisfying solution before that, but before carry on with the search, notice that already some some pruning got done in the form of nodes that won't exist, for example queens[4] can only take the values 2,4,7,8 at this point, and we haven't made any decision on it, its just the solver eliminating values from the variable that it knows will certainly result in failures, if we where making a brute force search this variable would have the same domain as in the root node [1..8] because we haven't made a decision on it yet, so we are making a smarter search by propagating the constraints.
3: First Failure: but we carry on
Carrying on with the same strategy it makes a split for queens[6], this time the minimum value queens[6] = 5, when propagating to the undecided variables, but there is no solution that satisfies all the constraints (here it gave the value 8 to two queens), so this is a dead end and must backtrack.
queens = array1d(1..8, [7, 2, 4, 8, 8, 5, 3, 1]); ---> Failure
So the very first three nodes of the search lead to a failure.
The search continues like that, since the choice for queens[6] = 5 caused a failure it goes to the next value queens[6] = [6..8], that search also results in the failures that are encircled in red in the image of the Search Tree.
As you can probably guess by now the search strategy is something like go in the order of the variables and split the domain of the variables by picking the smallest value available and put the rest of the domain in another node, this in minizinc search annotations are called input_order and indomain_min.
Now we fast forward the search to the node labeled 4.
4: Prelude to a solution: are we there yet?
Here you can see that queens[8] = 1 (remains the same), queens[7] = 5 while in the node 2 it was queens[7] = 3, that means that all the possibilities where queens[8] = 1 and queens[7] = [3..4] where either evaluated or pruned, but all lead to failures.
queens = array1d(1..8, [{2,4,6..7}, {2..3,6}, {2..4,7}, {3..4,7}, {2,6}, 8, 5, 1]);
Then this node spited into queens[6] = 2 (left child) which lead to more failures and queens[6] = 6 (right child)
5: We struck gold: a feasible Solution !!
queens[2] = 6 propagated, and the result satisfied all the constraints, so we have a solution and we stop the search.
queens = array1d(1..8, [4, 2, 7, 3, 6, 8, 5, 1]);
Pruning
Arriving to the solution only required 47 nodes of the gigantic Whole Search Tree, the area inside the blue line is the search tree is the Search Tree where nodes labeled 0,1,2,3,4,5 are, it is gigantic even pruned for this relatively small instance of 8 decision variables of cardinality 8 with a global constraint which certainly reduces the span of the search tree by a lot since it communicates the domains of the variables between each other much more effectively than the constraint store of the solver. The whole search tree only has 723 nodes in total (nodes and leafs) where only 362 are leafs, while the brute force search could generate all the possible 8^8 leaf nodes directly (again, it might not, but it could), thats a search space of 16.777.216 possibilities (its like 8 octal digits since its 8 variables with cardinality of domain 8), it is a big saving when you compare it, of the 16.777.216 to the solver only 362 made some sense, and 92 where feasible, its less than 0.0001% of the combinations of the whole search space you would face by, for example, generating at random a solution by generating 8 random digits in the range [1..8] and evaluating its feasibility afterwards, talk about a needle in a haystack.
Pruning basically means to reduce the search space, anything better than the evaluation of ALL the combinations, even by removing one single possibility is considered a pruned search space. Since this was a satisfaction problem rather than an optimization one, the pruning is just to remove unfeasible values from the domain of the variables.
In the optimization problems there are two types of pruning, the satisfaction pruning like before, eliminating imposible solutions, and the pruning done by the bounds of the objective function, when the bounds of the objective function can be determined before all the variables reached a value and be it is determined to be "worst" than the current "best" value found so far (i.e. in a minimization optimization the smallest value the objective could take in a branch is larger than the smallest value found so far in a feasible solution) you can prune that branch, which surely contains feasible (but not as good) solutions as well as unfeasible solutions, and save some work, also you still have to prune or evaluate all the tree if you want to find the optimal solution and prove that it is optimal.
To explore search trees like the ones of the images you can run your code with the gecode-gist solver in the minizinc IDE, or use minizinc --Solver gecode-gist <modelFile> <dataFile> in the command line, upon double clicking on one of the nodes you will see the state of the decision variables just like the ones in this post.
And even further use solve :: int_search( pos, varChoise, valChoise, complete) satisfy; to test this different search strategies
% variable selections:
ann : varChoise
% = input_order
% = first_fail
% = smallest
% = largest
;
% value selections:
ann : valChoise
% = indomain_min
% = indomain_max
% = indomain_median
% = indomain_random
% = indomain_split
% = indomain_reverse_split
;
just paste this in you model and uncomment one varChoise annotation and one valChoise to test that combination of variable selection and value selection, and see if one strategy finds the solution with less failures, less nodes, or less propagations. You can read more about them in the minizinc documentation.

Why would more array accesses perform better?

I'm taking a course on coursera that uses minizinc. In one of the assignments, I was spinning my wheels forever because my model was not performing well enough on a hidden test case. I finally solved it by changing the following types of accesses in my model
from
constraint sum(neg1,neg2 in party where neg1 < neg2)(joint[neg1,neg2]) >= m;
to
constraint sum(i,j in 1..u where i < j)(joint[party[i],party[j]]) >= m;
I dont know what I'm missing, but why would these two perform any differently from eachother? It seems like they should perform similarly with the former being maybe slightly faster, but the performance difference was dramatic. I'm guessing there is some sort of optimization that the former misses out on? Or, am I really missing something and do those lines actually result in different behavior? My intention is to sum the strength of every element in raid.
Misc. Details:
party is an array of enum vars
party's index set is 1..real_u
every element in party should be unique except for a dummy variable.
solver was Gecode
verification of my model was done on a coursera server so I don't know what optimization level their compiler used.
edit: Since minizinc(mz) is a declarative language, I'm realizing that "array accesses" in mz don't necessarily have a direct corollary in an imperative language. However, to me, these two lines mean the same thing semantically. So I guess my question is more "Why are the above lines different semantically in mz?"
edit2: I had to change the example in question, I was toting the line of violating coursera's honor code.
The difference stems from the way in which the where-clause "a < b" is evaluated. When "a" and "b" are parameters, then the compiler can already exclude the irrelevant parts of the sum during compilation. If "a" or "b" is a variable, then this can usually not be decided during compile time and the solver will receive a more complex constraint.
In this case the solver would have gotten a sum over "array[int] of var opt int", meaning that some variables in an array might not actually be present. For most solvers this is rewritten to a sum where every variable is multiplied by a boolean variable, which is true iff the variable is present. You can understand how this is less efficient than an normal sum without multiplications.

Transportation problem to minimize the cost using genetic algorithm

I am new to Genetic Algorithm and Here is a simple part of what i am working on
There are factories (1,2,3) and they can server any of the following customers(ABC) and the transportation costs are given in the table below. There are some fixed cost for A,B,C (2,4,1)
A B C
1 5 2 3
2 2 4 6
3 8 5 5
How to solve the transportation problem to minimize the cost using a genetic algorithm
First of all, you should understand what is a genetic algorithm and why we call it like that. Because we act like a single cell organism and making cross overs and mutations to reach a better state.
So, you need to implement your chromosome first. In your situation, let's take a side, customers or factories. Let's take customers. Your solution will look like
1 -> A
2 -> B
3 -> C
So, your example chromosome is "ABC". Then create another chromosome ("BCA" for example)
Now you need a fitting function which you wish to minimize/maximize.
This function will calculate your chromosomes' breeding chance. In your situation, that'll be the total cost.
Write a function that calculates the cost for given factory and given customer.
Now, what you're going to do is,
Pick 2 chromosomes weighted randomly. (Weights are calculated by fitting function)
Pick an index from 2 chromosomes and create new chromosomes via using their switched parts.
If new chromosomes have invalid parts (Such as "ABA" in your situation), make a fixing move (Make one of "A"s, "C" for example). We call it a "mutation".
Add your new chromosome to the chromosome set if it wasn't there before.
Go to first process again.
You'll do this for some iterations. You may have thousands of chromosomes. When you think "it's enough", stop the process and sort the chromosome set ascending/descending. First chromosome will be your result.
I'm aware that makes the process time/chromosome dependent. I'm aware you may or may not find an optimum (fittest according to biology) chromosome if you do not run it enough. But that's called genetic algorithm. Even your first run and second run may or may not produce the same results and that's fine.
Just for your situation, possible chromosome set is very small, so I guarantee that you will find an optimum in a second or two. Because the entire chromosome set is ["ABC", "BCA", "CAB", "BAC", "CBA", "ACB"] for you.
In summary, you need 3 informations for applying a genetic algorithm:
How should my chromosome be? (And initial chromosome set)
What is my fitting function?
How to make cross-overs in my chromosomes?
There are some other things to care about this problem:
Without mutation, genetical algorithm can stuck to a local optimum. It still can be used for optimization problems with constraints.
Even if a chromosome exists with a very low chance to be picked for cross-over, you shouldn't sort and truncate the chromosome set till the end of iterations. Otherwise, you may stuck at a local extremum or worse, you may get an ordinary solution candidate instead of global optimum.
To fasten your process, pick non-similar initial chromosomes. Without enough mutation rate, finding global optimum could be a real pain.
As mentioned in nejdetckenobi's answer, in this case the solution search space is too small, i.e. only 8 feasible solutions ["ABC", "BCA", "CAB", "BAC", "CBA", "ACB"]. I assume this is only a simplified version of your problem, and your problem actually contains more factories and customers (but the numbers of factories and customers are equal). In this case, you can just make use of special mutation and crossover to avoid infeasible solution with repeating customers, e.g. ["ABA", 'CCB', etc.].
For mutation, I suggest to use a swap mutation, i.e. randomly pick two customers, swap their corresponding factory (position):
ABC mutate to ACB
ABC mutate to CBA

Change (0, 1] to (0, 1) without branching

I have a random number generator that outputs values from (0, 1], but I need to give the output to a function that returns infinity at 0 or 1. How can I post-process the generated number to be in (0, 1) without any branches, as this is intended to execute on a GPU?
I suppose one way is to add a tiny constant and then take the value mod 1. In other words, generate from (ɛ, 1 + ɛ], which gets turned into [ɛ, 1). Is there a better way? What should the ɛ be?
Update 1
In Haskell, you can find ɛ by using floatRange. The C++ portion below applies otherwise.
Note: The answer below was written before the OP expressed the answer should be for Haskell
You don't state the implementation language in the question, so I'm going to assume C++ here.
Take a look at std::nextafter.
This will allow you to get the next possible value which you can add to the upper limit, which will result in your code acting as if it was inclusive.
As for the branching, you could overload the function to avoid the branch. However, this leads to code duplication.
I'd recommend allowing the branch and letting the compiler make such micro-optimizations unless you really need the performance and can provide a more specialised implementation than the standard one (see Pascal Cuoq's comment).

how to use state machine that invalidate value like "0.10"

Above is the state machine I copied from
http://discuss.leetcode.com/questions/241/valid-number
I think it cannot invalid values like "0.10".
It is possible to create a state machine that can invalidate values does not ends of "0"?
Generalizing from your single example, I assume that you want to refuse that the fractional part ends in 0.
The place to do that is around state 4, which is where the digits of the fractional part are processed, and can be ended by e/E or a space.
You will need to distinguish the 0 from other digits, and duplicate this state: 4 for nonzero digits, 4° for 0. Also duplicate/adapt the incoming transitions (1->4, 2->4 and 4->4). Rejection will be achieved by omitting 4°->5 and 4°->8 and not letting 4° be an accepting state.
It is certainly possible. To disallow a trailing zero completely, a straightforward approach would be to duplicate each terminal node into two: the one reachable with digits 1 to 9 and the one reachable with 0. After that, adjust the transitions accordingly.
On the other hand, by the nature of your question, it looks like you will also want to disallow numbers such as 0. and maybe others. What exactly is allowed would require a rigorous definition, and while messing with the existing state machine is certainly possible, it may turn out your rules are actually simpler than the ones used in it. And in that case, you will be better off just constructing a new state machine from scratch according to your definition.

Resources