JACK: Java Constraint Kit

Home - Documentation - Sources - Contributors - Related Links

 

Documentation


JASE

For the search, an exploration is used to walk through a search tree which consists of nodes; the behaviour of the nodes is implemented by choices.

1. Search concepts and interfaces

back


2. How to run a search in the Finite Domain handler?

To run a search, the constraint system and variables must have been created in Java code, as described in Interface JCHR - Java. The following code will enumerate the variable X, Y and Z:
        ObjectContainer vars=new ObjectContainer();
        vars.add(X);
        vars.add(Y);
        vars.add(Z);

        SChoice collector=new SCollectorChoice(vars,cs.getVariableNames(vars));

        SChoice rootChoice=SFDChoice.enumeration(vars,
                       cs.getVariableNames(vars),0,collector,trace);

        SExploration exploration=new SDepthFirstExploration(cs,rootChoice);

        boolean success=SSearch.all(exploration);       

        System.out.println(((SCollectorChoice)collector).toBeautifulString());
    
This code can be used as a simple search template. For an explanation of the classes used, see Classes and interfaces for using search. Different search strategies (other than depth-first) can be used too, see Branch and bound and Limited discrepancy search.

back


3. Classes and interfaces for using search

These classes and interfaces are directly usable for a search:

SChoice

This is the interface which must be implemented by all choices. There are several predefined implementations of this in the JACK system, which are distinguished as root choices, continuation choices and special choices.

SDepthFirstExploration

This class implements depth-first exploration. It has no methods for the end user. The constructor is this:

public SDepthFirstExploration(ConstraintSystem system,SChoice choice)

SSearch

This class has several static methods for actually running the search:

back


4. Branch and bound

There are special classes in the JACK system which do branch-and-bound search. Instead of just walking through the whole search tree, this algorithm modifies the constraint system after a solution is found, and restarts the search. The modification is to insert a new constraint which assures that any further solution is better than the last solution.

The standard search template must be modified to use branch and bound:

  1. A special continuation choice must be used:
    SBestSearchCollector continuation=new MyCollector(...);
    
    The class MyCollector must implement the following method:

    public void addBetterConstraint(ConstraintSystem system)

    This method must insert a new constraint into the constraint system, which takes the currently "best" solution into account and guarantees that any further solution will be better.

    For example, this class would maximize the first variable of a list of variables:

        class MaxCollector extends SBestSearchCollector
        {
    	private Object theVariable;
    
    	MaxCollector(ObjectContainer variables,ObjectContainer variableNames)
    	{
    	    super(variables,variableNames);
    	    theVariable=variables.get(0);
    	}
    
    	public void addBetterConstraint(ConstraintSystem cs)
    	{
    	    ObjectContainer bestSolution=getSolution();
    	    Object value=bestSolution.get(0);
     	    cs.addGoalConstraint(new FDLTConstraint(value,theVariable);
    	}
        }
    	

  2. A special method of SSearch must be used:

        boolean success=SSearch.best(exploration,continuation,cs);
    

  3. To retrieve the final solution, the method public ObjectContainer getSolution() of the continuation must be used. It returns a list of variable bindings which corresponds to the list of variables passed in to the constructor of SBestSearchCollector.

The complete code template for using branch-and-bound looks like this:

        ObjectContainer vars=new ObjectContainer();
        vars.add(X);
        vars.add(Y);
        vars.add(Z);

	SBestSearchCollector coll=new MaxCollector(variables,variableNames);

        SChoice rootChoice=SFDChoice.enumeration(vars,
                       cs.getVariableNames(vars),0,collector,trace);
	
	SExploration exploration=new SDepthFirstExploration(cs,rootChoice);

	boolean success=SSearch.best(exploration,coll,cs);

 	System.out.println(((SBestSearchCollector)coll).toBeautifulString());

back


5. Limited discrepancy search

Limited discrepancy search (LDS) has been implemented in the standard JACK distribution, too. LDS is a variant of the depth-first search which changes the order in which the nodes are visited. It assumes that the heuristic used to chose the children of a node is very good, that is, that the first child will lead to the "wanted" solution more often than not.

If the heuristic would be perfect, the solution would immediately be found when walking along the left-most path of the search tree. This path is assigned a discrepancy count of 0. If, along another path in the search tree, there is exactly one node where the edge to the second instead of the first child is followed, we assign it a discrepancy count of 1 (and so on). Thus, the last path through the tree, which follows the second child in every node, has maximal discrepancy count.

LDS now enumerates all solutions ordered by their discrepancy count. It does this by limiting the allowed discrepancies - if, during the search, a node has a higher discrepancy than allowed, it (and its subtree) is removed. The search is first run with a small allowed discrepancy, and this is increased until a solution (or some maximum discrepancy) is reached (a more detailled description of LDS).

To use LDS, the class SDiscrepancyExploration must be used instead of SDepthFirstExploration, using this constructor:

public SDiscrepancyExploration(ConstraintSystem system,SChoice choice,int startingMaxDisc,int discLimit)

A template for using LDS is:

        ObjectContainer vars=new ObjectContainer();
        vars.add(X);
        vars.add(Y);
        vars.add(Z);

        SChoice collector=new SCollectorChoice(vars,cs.getVariableNames(vars));

        SChoice rootChoice=SFDChoice.enumeration(vars,
                       cs.getVariableNames(vars),0,collector,trace);

        SExploration exploration=new
           SDiscrepancyExploration(cs,rootChoice,0,100);

        boolean success=SSearch.all(exploration);       

        System.out.println(((SCollectorChoice)collector).toBeautifulString());
    

back


6. Classes and interfaces for implementing new search algorithms

There are several ways to create new search algorithms. First of all, and in most cases, you will want to modify an existing algorithm slightly, or add functionality to one. Most ways to extend the "simple" depth first search are already available in the JASE toolkit (for example, LDS search which changes the SDepthFirstExploration, or Branch and Bound, which uses a custom continuation SChoice).

For details on the implementation, and an thorough explanation of the background of JASE, please refer to the diploma thesis of JASE.

6.1. Implementing a new SChoice

Since each different JCHR handler needs its own SChoice, this class is very interesting in most cases. See the enumeration choice of the Finite Domain handler for an example.

Things to do when implementing a new instance of SChoice:

6.2. Implementing a new SExploration

If you want to create a new or modified way to walk through the search tree, you have to implement a new instance of SExploration. This interface has one important method,
public SNode performOneStep()
which does exactly what its name implies - perform one transition from the current node in the search tree to the next node (where the definition of "next" depends on the algorithm to be implemented).

In most cases, you can use the existing (trivial) depth first implementation and modify it.

6.3. Changing the way in which results are processed

Results of a search are processed by continuation SChoice's. If you need more functionality than just collecting the results or printing them out, then override the abstract class SChoiceOperator and implement its method
abstract void run(ConstraintSystem system)
An object of this class can be passed to any search, and the run method will be called whenever a solution is found. See the section on continuation choices for more information.

back


Last modified: Mon Sep 17 10:26:26 CEST 2001