From 70efd85576c959a10c41c66b5926becc54e35518 Mon Sep 17 00:00:00 2001 From: VIGNET Pierre <pierre.vignet@irisa.fr> Date: Mon, 2 Dec 2019 16:57:26 +0100 Subject: [PATCH] [lib] C: Refactor shift_clause & forward_code Remove calls to python functions and replace them with much more efficient pure C functions --- library/_cadbiom/cadbiom.c | 268 ++++++++++++++++++++++--------------- 1 file changed, 159 insertions(+), 109 deletions(-) diff --git a/library/_cadbiom/cadbiom.c b/library/_cadbiom/cadbiom.c index ff90048..58aed70 100644 --- a/library/_cadbiom/cadbiom.c +++ b/library/_cadbiom/cadbiom.c @@ -116,55 +116,13 @@ int raw_get_unshift_code(int* var_num, int* shift_step) return var_code * ((*var_num < 0) ? -1 : 1); } -PyDoc_STRVAR(shift_clause_doc, -"shift_clause(numeric_clause, shift_step)\n\ -Implementation of the shift operator. Shift a numeric clause in one step.\n\ -\n\ -Basically, `shift_step` is added to positive variables and subtracted\n\ -from negative variables in `numeric_clause`.\n\ -\n\ -.. warning:: Before the call you MUST lock the unfolder with:\n\ -\"self.__locked = True\".\n\ -\n\ -:param numeric_clause: DIMACS clause (list of literals)\n\ -:param shift_step: Shift step dependant on the run\n\ -:return: DIMACS literal coding of x_0 with same value\n\ -:type numeric_clause: <list <int>>\n\ -:type shift_step: <int>\n\ -:rtype: <list>" -); - -static PyObject* shift_clause(PyObject *self, PyObject *args, PyObject *kwds) +static int _shift_clause(PyObject *self, PyObject *numeric_clause, PyObject *shifted_clause, int* shift_step) { - // https://docs.python.org/2/c-api/iter.html - #ifndef NDEBUG - /* Debugging code */ - printf("shift clause\n"); - #endif - - int shift_step; - PyObject *numeric_clause; - - static char* kwlist[] = {"numeric_clause", "shift_step", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oi", kwlist, - &numeric_clause, - &shift_step)) { - return NULL; - } - PyObject *numeric_clause_iterator = PyObject_GetIter(numeric_clause); if (numeric_clause_iterator == NULL) { /* propagate error */ PyErr_SetString(PyExc_SystemError, "could not create iterator on clause"); - return NULL; - } - - // Declare a new list with the size of the given one - PyObject *shifted_clause = PyList_New(PySequence_Size(numeric_clause)); - if (shifted_clause == NULL) { - PyErr_SetString(PyExc_SystemError, "failed to create a list"); - return NULL; + return 0; } PyObject *lit; @@ -176,7 +134,9 @@ static PyObject* shift_clause(PyObject *self, PyObject *args, PyObject *kwds) /* Debugging code */ if (!IS_INT(lit)) { PyErr_SetString(PyExc_TypeError, "integer expected in list"); - return NULL; + Py_DECREF(numeric_clause_iterator); + Py_DECREF(lit); + return 0; } #endif @@ -185,15 +145,15 @@ static PyObject* shift_clause(PyObject *self, PyObject *args, PyObject *kwds) if (lit_val > 0) { #ifdef IS_PY3K - shifted_lit = PyLong_FromLong(lit_val + shift_step); + shifted_lit = PyLong_FromLong(lit_val + *shift_step); #else - shifted_lit = PyInt_FromLong(lit_val + shift_step); + shifted_lit = PyInt_FromLong(lit_val + *shift_step); #endif } else { #ifdef IS_PY3K - shifted_lit = PyLong_FromLong(lit_val - shift_step); + shifted_lit = PyLong_FromLong(lit_val - *shift_step); #else - shifted_lit = PyInt_FromLong(lit_val - shift_step); + shifted_lit = PyInt_FromLong(lit_val - *shift_step); #endif } @@ -215,73 +175,88 @@ static PyObject* shift_clause(PyObject *self, PyObject *args, PyObject *kwds) if (PyErr_Occurred()) { /* propagate error */ - Py_DECREF(shifted_clause); - return PyErr_SetFromErrno(outofconflerr); + return 0; } - - // Return list of all shifted literals - return shifted_clause; + return 1; } -PyDoc_STRVAR(forward_code_doc, -"forward_code(clause, var_code_table, shift_step)\n\ -Translation from names to num codes. The translation depends on the shift direction.\n\ +PyDoc_STRVAR(shift_clause_doc, +"shift_clause(numeric_clause, shift_step)\n\ +Implementation of the shift operator. Shift a numeric clause in one step.\n\ \n\ -Numerically code a clause with the numeric codes found in self.__var_code_table for\n\ -a base variable x and numeric_code + shift_step for x'\n\ -variable integer coding increases in future steps.\n\ +Basically, `shift_step` is added to positive variables and subtracted\n\ +from negative variables in `numeric_clause`.\n\ \n\ -..note:: Future variables x' are noted \"name`\" in Literal names.\n\ - Values of variables increases of __shift_step in future steps.\n\ +.. warning:: Before the call you MUST lock the unfolder with:\n\ +\"self.__locked = True\".\n\ \n\ -:param clause: a Clause object\n\ -:param var_code_table: Var code table from the model\n\ +:param numeric_clause: DIMACS clause (list of literals)\n\ :param shift_step: Shift step dependant on the run\n\ -:return: the DIMACS coding of the forward shifted clause\n\ -:type clause: <list <int>>\n\ -:type var_code_table: <dict>\n\ +:return: DIMACS literal coding of x_0 with same value\n\ +:type numeric_clause: <list <int>>\n\ :type shift_step: <int>\n\ :rtype: <list>" ); -static PyObject* forward_code(PyObject *self, PyObject *args, PyObject *kwds) +static PyObject* shift_clause(PyObject *self, PyObject *args, PyObject *kwds) { + // https://docs.python.org/2/c-api/iter.html #ifndef NDEBUG /* Debugging code */ - printf("forward code\n"); + printf("shift clause\n"); #endif int shift_step; - PyObject *clause; - PyObject *var_code_table; + PyObject *numeric_clause; - static char* kwlist[] = {"clause", "var_code_table", "shift_step", NULL}; + static char* kwlist[] = {"numeric_clause", "shift_step", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOi", kwlist, - &clause, - &var_code_table, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "Oi", kwlist, + &numeric_clause, &shift_step)) { return NULL; } + // Declare a new list with the size of the given one + PyObject *shifted_clause = PyList_New(PySequence_Size(numeric_clause)); + if (shifted_clause == NULL) { + PyErr_SetString(PyExc_SystemError, "failed to create a list"); + return NULL; + } + + // Shift clause and fill shifted_clause (new ref) + int ret = _shift_clause(self, numeric_clause, shifted_clause, &shift_step); + if (ret == 0 || PyErr_Occurred()) { + /* propagate error */ + Py_XDECREF(shifted_clause); + return PyErr_SetFromErrno(outofconflerr); + } + + // Return list of all shifted literals + return shifted_clause; +} + +static int _forward_code(PyObject *self, PyObject *clause, PyObject **numeric_clause, PyObject *var_code_table, int* shift_step) +{ + // Get attr literals of clause object PyObject* literals = PyObject_GetAttrString(clause, "literals"); if (literals == NULL) { PyErr_SetString(PyExc_SystemError, "failed get attribute 'literals' on clause object"); - return NULL; + return 0; } PyObject *literals_iterator = PyObject_GetIter(literals); if (literals_iterator == NULL) { PyErr_SetString(PyExc_SystemError, "could not create iterator on 'literals' attribute"); - return NULL; + return 0; } // Declare a new list with the size of 'literals' - PyObject *numeric_clause = PyList_New(PySequence_Size(literals)); - if (numeric_clause == NULL) { + *numeric_clause = PyList_New(PySequence_Size(literals)); + if (*numeric_clause == NULL) { PyErr_SetString(PyExc_SystemError, "failed to create a list"); - return NULL; + return 0; } // Append does not steal a reference, so we need Py_DECREF // SET_ITEM steals a reference, so: no Py_DECREF on items ! @@ -295,12 +270,10 @@ static PyObject* forward_code(PyObject *self, PyObject *args, PyObject *kwds) PyObject *multiply_result; #ifdef IS_PY3K PyObject *last_char = PyUnicode_FromString("`"); - PyObject *py_shift_step = PyLong_FromLong(shift_step); - PyObject *minus_one = PyLong_FromLong(-1); + PyObject *py_shift_step = PyLong_FromLong(*shift_step); #else PyObject *last_char = PyString_FromString("`"); - PyObject *py_shift_step = PyInt_FromLong(shift_step); - PyObject *minus_one = PyInt_FromLong(-1); + PyObject *py_shift_step = PyInt_FromLong(*shift_step); #endif Py_ssize_t i = 0; Py_ssize_t name_size = 0; @@ -320,8 +293,16 @@ static PyObject* forward_code(PyObject *self, PyObject *args, PyObject *kwds) // Borrowed reference variable_value = PyDict_GetItem(var_code_table, lit_name); if (variable_value == NULL) { + Py_DECREF(lit_name); + Py_DECREF(lit_sign); + Py_DECREF(lit); + + Py_DECREF(literals); + Py_DECREF(literals_iterator); + Py_DECREF(last_char); + Py_DECREF(py_shift_step); PyErr_SetString(PyExc_KeyError, "lit_name not found in dict"); - return NULL; + return 0; } // Because variable_value reference IS borrowed (owned by PyDict_GetItem) // we don't do Py_DECREF(variable_value); @@ -334,7 +315,7 @@ static PyObject* forward_code(PyObject *self, PyObject *args, PyObject *kwds) // t+1 variable // Add shift_step to the value corresponding to the given name - lit_val = PyLong_AsLong(variable_value) + shift_step; + lit_val = PyLong_AsLong(variable_value) + *shift_step; // Ret of PyObject_Not is: 1 if lit is false, -1 on failure #ifdef IS_PY3K @@ -358,7 +339,7 @@ static PyObject* forward_code(PyObject *self, PyObject *args, PyObject *kwds) } // Because PyList_SET_ITEM steals the reference of multiply_result, we don't do Py_DECREF(multiply_result); - PyList_SET_ITEM(numeric_clause, i, multiply_result); + PyList_SET_ITEM(*numeric_clause, i, multiply_result); // Note: // Add new lit_cod (use SET_ITEM instead of PyList_Append) @@ -381,11 +362,62 @@ static PyObject* forward_code(PyObject *self, PyObject *args, PyObject *kwds) Py_DECREF(literals_iterator); Py_DECREF(last_char); Py_DECREF(py_shift_step); - Py_DECREF(minus_one); if (PyErr_Occurred()) { /* propagate error */ - Py_DECREF(numeric_clause); + return 0; + } + return 1; +} + +PyDoc_STRVAR(forward_code_doc, +"forward_code(clause, var_code_table, shift_step)\n\ +Translation from names to num codes. The translation depends on the shift direction.\n\ +\n\ +Numerically code a clause with the numeric codes found in self.__var_code_table for\n\ +a base variable x and numeric_code + shift_step for x'\n\ +variable integer coding increases in future steps.\n\ +\n\ +..note:: Future variables x' are noted \"name`\" in Literal names.\n\ + Values of variables increases of __shift_step in future steps.\n\ +\n\ +:param clause: a Clause object\n\ +:param var_code_table: Var code table from the model\n\ +:param shift_step: Shift step dependant on the run\n\ +:return: the DIMACS coding of the forward shifted clause\n\ +:type clause: <list <int>>\n\ +:type var_code_table: <dict>\n\ +:type shift_step: <int>\n\ +:rtype: <list>" +); + +static PyObject* forward_code(PyObject *self, PyObject *args, PyObject *kwds) +{ + #ifndef NDEBUG + /* Debugging code */ + printf("forward code\n"); + #endif + + int shift_step; + PyObject *clause; + PyObject *var_code_table; + + static char* kwlist[] = {"clause", "var_code_table", "shift_step", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOi", kwlist, + &clause, + &var_code_table, + &shift_step)) { + return NULL; + } + + PyObject *numeric_clause = NULL; + + // Shift clause and fill shifted_clause (new ref) + int ret = _forward_code(self, clause, &numeric_clause, var_code_table, &shift_step); + if (ret == 0 || PyErr_Occurred()) { + /* propagate error */ + Py_XDECREF(numeric_clause); return PyErr_SetFromErrno(outofconflerr); } @@ -460,18 +492,25 @@ static PyObject* forward_init_dynamic(PyObject *self, PyObject *args, PyObject * } PyObject *clause; - PyObject *arglist; Py_ssize_t i = 0; + PyObject *numeric_clause = NULL; while ((clause = PyIter_Next(clauses_iterator)) != NULL) { - // Build parameters of forward_code - // {"clause", "var_code_table", "shift_step", NULL}; - arglist = Py_BuildValue("(OOi)", clause, var_code_table, shift_step); - PyList_SET_ITEM(numeric_clauses, i, forward_code(self, arglist, NULL)); + // Forward code the non numeric clause to numeric_clause (new ref) + int ret = _forward_code(self, clause, &numeric_clause, var_code_table, &shift_step); + if (ret == 0 || PyErr_Occurred()) { + /* propagate error */ + Py_XDECREF(numeric_clause); + Py_DECREF(clauses_iterator); + Py_DECREF(clause); + Py_DECREF(numeric_clauses); + return PyErr_SetFromErrno(outofconflerr); + } + + PyList_SET_ITEM(numeric_clauses, i, numeric_clause); /* release reference when done */ // no DECREF on the return of forward_code, because PyList_SET_ITEM steals reference - Py_DECREF(arglist); Py_DECREF(clause); i++; @@ -491,14 +530,21 @@ static PyObject* forward_init_dynamic(PyObject *self, PyObject *args, PyObject * while ((clause = PyIter_Next(clauses_iterator)) != NULL) { - // Build parameters of forward_code - // {"clause", "var_code_table", "shift_step", NULL}; - arglist = Py_BuildValue("(OOi)", clause, var_code_table, shift_step); - PyList_SET_ITEM(numeric_clauses, i, forward_code(self, arglist, NULL)); + // Forward code the non numeric clause to numeric_clause (new ref) + int ret = _forward_code(self, clause, &numeric_clause, var_code_table, &shift_step); + if (ret == 0 || PyErr_Occurred()) { + /* propagate error */ + Py_XDECREF(numeric_clause); + Py_DECREF(clauses_iterator); + Py_DECREF(clause); + Py_DECREF(numeric_clauses); + return PyErr_SetFromErrno(outofconflerr); + } + + PyList_SET_ITEM(numeric_clauses, i, numeric_clause); /* release reference when done */ // no DECREF on the return of forward_code, because PyList_SET_ITEM steals reference - Py_DECREF(arglist); Py_DECREF(clause); i++; @@ -585,21 +631,25 @@ static PyObject* shift_dynamic(PyObject *self, PyObject *args, PyObject *kwds) PyObject *shifted_clause; PyObject *clause; - PyObject *arglist; Py_ssize_t i = 0; while ((clause = PyIter_Next(numeric_clauses_iterator)) != NULL) { - // Build parameters of shift_clause - // shifted_clause = shift_clause(self, clause, shift_step); - arglist = Py_BuildValue("(Oi)", clause, shift_step); - shifted_clause = shift_clause(self, arglist, NULL); // new ref - Py_DECREF(arglist); + // Declare a new list with the size of the given one + shifted_clause = PyList_New(PySequence_Size(clause)); + if (shifted_clause == NULL) { + PyErr_SetString(PyExc_SystemError, "failed to create a list"); + return NULL; + } - if(shifted_clause == NULL) { - /* Pass error back */ + // Shift clause and fill shifted_clause (new ref) + int ret = _shift_clause(self, clause, shifted_clause, &shift_step); + if (ret == 0 || PyErr_Occurred()) { + /* propagate error */ + Py_XDECREF(shifted_clause); Py_DECREF(shifted_numeric_clauses); Py_DECREF(numeric_clauses_iterator); - return NULL; + Py_DECREF(clause); + return PyErr_SetFromErrno(outofconflerr); } PyList_SET_ITEM(shifted_numeric_clauses, i, shifted_clause); -- GitLab