.. _debugging-and-tracing: ********************* 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 .. method:: 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. .. method:: printVariablesStates() Print the domains of variables linked to the problem. .. method:: printDisjunctionStates() Print the list of all the disjunctions posted to the problem. .. method:: printMinimalConflictSet() Print the minimal conflict set of the problem. .. method:: computeMinimalConflictSet() Return a :cpp:func:`KConstraintArray*` of the minimal conflict set of the problem. Example of use : .. tabs:: .. code-tab:: c++ problem.pushWorld(); if (problem.propagate()) { printf("Problem is infeasible\n"); problem.popWorld(); problem.printMinimalConflictSet(); } .. code-tab:: py problem.pushWorld() if problem.propagate(): print("Problem is infeasible") problem.popWorld() problem.printMinimalConflictSet() .. code-tab:: java problem.pushWorld(); if (problem.propagate()) { System.out.println("Problem is infeasible"); problem.popWorld(); problem.printMinimalConflictSet(); } .. .. class:: KIntVar .. method:: print() Print the name of the variable and output its current domain (possibly reduced by propagation). .. class:: KConstraint .. method:: 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 :ref:`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 :cpp:func:`KSolver::setSolutionFunctionPtr` The solver opens a node :cpp:func:`KSolver::setNodeFunctionPtr` The solver goes down/up a branch :cpp:func:`KSolver::setBranchFunctionPtr` A new branching scheme is used :cpp:func:`KSolver::setBranchingSchemeFunctionPtr` ================================ ================================================== The following code sample shows how to define and specify a function :cpp:func:`solution_found` that will be called when a solution will be found by the solver: .. tabs:: .. code-tab:: c++ int solution_found(void *param) { KProblem * p = (KProblem *) param; p->getSolution().printResume(); return 0; } KSolver solver(); solver.setSolutionFunctionPtr(solution_found,&problem); .. code-tab:: py # 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) .. _UsingKSolverEventListenerSection: 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 :cpp:func:`KSolverEventListener::solutionFound` The solver opens a node :cpp:func:`KSolverEventListener::nodeExplored` The solver goes down a branch :cpp:func:`KSolverEventListener::branchGoDown` The solver goes up a branch :cpp:func:`KSolverEventListener::branchGoUp` A new branching scheme is used :cpp:func:`KSolverEventListener::branchingSchemeSwitch` ================================ ======================================================= In addition, the method :cpp:func:`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 :cpp:func:`KSolver::SearchLimitReached` will be set to the value :cpp:func:`KSolver::SearchLimitedByUser`. The following code sample shows how to define and specify a listener :cpp:class:`SolutionListener` that will display each solution found and that will terminate the search if at least 5 solutions have been found: .. tabs:: .. code-tab:: c++ 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); .. code-tab:: py 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) .. code-tab:: java 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()); Total time spent in user callbacks can be retrieved from the solver attributes: .. tabs:: .. code-tab:: c++ double cbTime = solver.getDblAttrib(KSolver::CallBackTime); .. code-tab:: py cbTime = solver.getDblAttrib(solver.CallBackTime) .. code-tab:: java double cbTime = solver.getDblAttrib(KSolver.DblAttrib.CallBackTime); 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: .. tabs:: .. code-tab:: c++ try { /* Your application code goes here */ } catch (ArtelysException &artelysException) { cout << "Exception " << artelysException.getCode() << " raised:" << artelysException.getMessage() << endl; } .. code-tab:: py try: # Your application code goes here except ArtelysException as e: print("Exception ", e.getCode(), " raised: ", e.getMessage()) .. code-tab:: java try { // Your application code goes here } catch (Exception e) { e.printStackTrace(); }