Debugging and tracing

In this chapter, you will learn how to use Artelys Kalis in debugging and tracing mode. The following parts will be discussed:

  • managing outputs ;
  • managing exceptions generated by Artelys Kalis.

Using print methods for debugging

Different methods for printing information on the current states of the variables and constraints are available for debugging purposes:

class KProblem
print()
Print the problem name.
Print the domains of variables linked to the problem.
Print the list of all the constraints posted to the problem.
Print the list of all the disjunctions posted to the problem and their status.

Note

The status of a disjunction can be either Known if the solver has proved that a branch of the disjunction must be true or false, or Unknown when the solver does not know yet the status of the branch.

printVariablesStates()

Print the domains of variables linked to the problem.

printDisjunctionStates()

Print the list of all the disjunctions posted to the problem.

printMinimalConflictSet()

Print the minimal conflict set of the problem.

computeMinimalConflictSet()

Return a KConstraintArray*() of the minimal conflict set of the problem.

Example of use :

problem.pushWorld();
if (problem.propagate()) {
    printf("Problem is infeasible\n");

    problem.popWorld();
    problem.printMinimalConflictSet();
}
problem.pushWorld()
if problem.propagate():
    print("Problem is infeasible")
    problem.popWorld()
    problem.printMinimalConflictSet()
problem.pushWorld();
if (problem.propagate()) {
    System.out.println("Problem is infeasible");

    problem.popWorld();
    problem.printMinimalConflictSet();
}
class KIntVar
print()

Print the name of the variable and output its current domain (possibly reduced by propagation).

class KConstraint
print()

Print the constraint name.

Using callbacks for debugging

Warning

The use of callbacks for debugging is deprecated since version 8.0. It will be removed in future versions. Instead users shall use KSolverEventListener to reproduce the callbacks functionalities in a more object-oriented manner (see next section).

Different callbacks functions can be specified in reaction to a specific event. The different callbacks and their corresponding events are listed in the following table:

Triggering event Method
A solution has been found KSolver::setSolutionFunctionPtr()
The solver opens a node KSolver::setNodeFunctionPtr()
The solver goes down/up a branch KSolver::setBranchFunctionPtr()
A new branching scheme is used KSolver::setBranchingSchemeFunctionPtr()

The following code sample shows how to define and specify a function solution_found() that will be called when a solution will be found by the solver:

int solution_found(void *param)
{
    KProblem * p = (KProblem *) param;
    p->getSolution().printResume();
    return 0;
}

KSolver solver();
solver.setSolutionFunctionPtr(solution_found,&problem);
# myPythonCallBack class is defined and derived from C++ class KPyCallBack.
# It has to implement a 'call' function taking no argument.
class MySolutionCallBack(KPyCallBack):
    def __init__(self, p):
        # Call C++ base class constructor
        KPyCallBack.__init__(self)
        self.problem = p
    def call(self):
        print('Solution callback called')
        self.problem.display()
        return 0

solver = KSolver(problem)
solutionCb = MySolutionCallBack(problem)
setSolutionFunctionPtr(solver, solutionCb)

Using KSolverEventListener

The KSolverEventListener class is an interface that should be implemented in order to debug or even control the search. The implemented method will be called in reaction to specific search events. The different listener methods and their corresponding events are listed in the following table:

Triggering event Method
A solution has been found KSolverEventListener::solutionFound()
The solver opens a node KSolverEventListener::nodeExplored()
The solver goes down a branch KSolverEventListener::branchGoDown()
The solver goes up a branch KSolverEventListener::branchGoUp()
A new branching scheme is used KSolverEventListener::branchingSchemeSwitch()

In addition, the method KSolverEventListener::stopComputations() can be implemented in order to control the termination of the search. This method is called at each node and shall return true if the search must be terminated. In such case, the integer attribute KSolver::SearchLimitReached() will be set to the value KSolver::SearchLimitedByUser().

The following code sample shows how to define and specify a listener SolutionListener that will display each solution found and that will terminate the search if at least 5 solutions have been found:

class SolutionListener: public KSolverEventListener
{
    SolutionListener(KProblem* problem): KSolverEventListener(problem) {}

    void solutionFound(const KSolution& solution)
    {
        solution.printResume();
    }

    bool stopComputations()
    {
        if (getProblem()->getNumberOfSolutions() >= 5)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
};

KProblem problem(/* … */);
KSolver solver(/* … */);
SolutionListener solutionListener(&problem);
solver.setSolverEventListener(&solutionListener);
class SolutionListener(KSolverEventListener):
    def __init__(self, problem: KProblem):
        KSolverEventListener.__init__(self, problem)

    def solutionFound(self, solution: KSolution, thread_id: int):
        solution.printResume()

    def stopComputations(self):
        if self.getProblem().getNumberOfSolutions() >= 5:
            return True
        else:
            return False

solver = KSolver(problem)
solutionListener = SolutionListener(problem)
solver.setSolverEventListener(solutionListener)
class MySolverEventListener extends KSolverEventListener
{
    public void nodeExplored() {
        System.out.println("Node explored");
    }

    public void branchGoDown() {
        System.out.println("Branch go down");
    }

    public void branchGoUp() {
        System.out.println("Branch go up");
    }

    public void branchingScheme() {
    }

    public boolean stopComputations() {
        return false;
    }

    public void solutionFound() {
        System.out.println("A solution has been found!");
    }
}

KSolver solver = new KSolver(problem,myBa);
solver.setSolverEventListener(new MySolverEventListener());

Managing exceptions

Artelys Kalis uses the standard C++ exception mechanism to inform your application that an exception was raised. Artelys Kalis offers a special exception object named ArtelysException that can be used to know what kind of error occured. ArtelysException gives two informations: an error code and an error message that describes the error.

To catch Artelys Kalis exceptions, just enclose your code like in the example:

try
{
    /* Your application code goes here */
}
catch (ArtelysException &artelysException)
{
    cout << "Exception " << artelysException.getCode() << " raised:" << artelysException.getMessage() << endl;
}
try:
    # Your application code goes here
except ArtelysException as e:
    print("Exception ", e.getCode(), " raised: ", e.getMessage())
try {
   // Your application code goes here
}
catch (Exception e)
{
    e.printStackTrace();
}