Path: blob/master/sage/combinat/matrices/dancing_links.pyx
4076 views
"""1Dancing Links internal pyx code2"""3#*****************************************************************************4# Copyright (C) 2008 Carlo Hamalainen <[email protected]>,5#6# Distributed under the terms of the GNU General Public License (GPL)7#8# This code is distributed in the hope that it will be useful,9# but WITHOUT ANY WARRANTY; without even the implied warranty of10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU11# General Public License for more details.12#13# The full text of the GPL is available at:14#15# http://www.gnu.org/licenses/16#*****************************************************************************1718#clang c++1920import sys2122include "../../ext/python_list.pxi"23include "../../ext/stdsage.pxi"24include "../../ext/python_int.pxi"25include "../../ext/python_ref.pxi"2627cdef extern from "dancing_links_c.h":28ctypedef struct vector_int "std::vector<int>":29void (* push_back)(int elem)30void clear()31int at(size_t loc)32int size()3334ctypedef struct vector_vector_int "std::vector<vector<int> >":35void (* push_back)(vector_int elem)3637ctypedef struct dancing_links:38vector_int solution39void add_rows(vector_vector_int rows)40int search()41void freemem()4243dancing_links* dancing_links_construct "Construct<dancing_links>"(void *mem)44void dancing_links_destruct "Destruct<dancing_links>"(dancing_links *mem)4546from sage.rings.integer cimport Integer4748cdef class dancing_linksWrapper:49cdef dancing_links x50cdef object rows5152def __init__(self, rows):53"""54Initialize our wrapper (self.x) as an actual C++ object.5556We must pass a list of rows at start up. There are no methods57for resetting the list of rows, so this class acts as a one-time58executor of the C++ code.5960TESTS:61sage: rows = [[0,1,2], [1, 2]]62sage: x = make_dlxwrapper(dumps(rows))63sage: loads(x.__reduce__()[1][0])64[[0, 1, 2], [1, 2]]65"""66pass6768# Note that the parameters to __cinit__, if any, must be identical to __init__69# This is due to the way Python constructs class instance.70def __cinit__(self, rows):71self.rows = PyList_New(len(rows))72dancing_links_construct(&self.x)73self.add_rows(rows)7475def __dealloc__(self):76self.x.freemem()77dancing_links_destruct(&self.x)7879def __str__(self):80"""81The string representation of this wrapper is just the list of82rows as supplied at startup.8384TESTS:85sage: rows = [[0,1,2]]86sage: print make_dlxwrapper(dumps(rows)).__str__()87[[0, 1, 2]]88"""8990return self.rows.__str__()9192def __reduce__(self):93"""94This is used when pickling.9596TESTS:97sage: rows = [[0,1,2]]98sage: x = make_dlxwrapper(dumps(rows))99sage: loads(x.__reduce__()[1][0])100[[0, 1, 2]]101"""102# A comment from sage/rings/integer.pyx:103104# This single line below took me HOURS to figure out.105# It is the *trick* needed to pickle Cython extension types.106# The trick is that you must put a pure Python function107# as the first argument, and that function must return108# the result of unpickling with the argument in the second109# tuple as input. All kinds of problems happen110# if we don't do this.111#return sage.rings.integer.make_integer, (self.str(32),)112113import sage.combinat.matrices.dancing_links114from sage.all import dumps115return sage.combinat.matrices.dancing_links.make_dlxwrapper, (dumps(self.rows),)116117def __richcmp__(dancing_linksWrapper left, dancing_linksWrapper right, int op):118"""119Two dancing_linksWrapper objects are equal if they were120initialised using the same row list.121122TESTS:123sage: from sage.combinat.matrices.dancing_links import dlx_solver124sage: rows = [[0,1,2]]125sage: X = dlx_solver(rows)126sage: Z = dlx_solver(rows)127sage: rows += [[2]]128sage: Y = dlx_solver(rows)129sage: X == Z1301131sage: X == Y1320133"""134135cdef int equal136equal = left.rows == right.rows137138if op == 2: # ==139return equal140elif op == 3: # !=141return not equal142else:143return NotImplemented144145def dumps(self):146"""147TESTS:148sage: from sage.combinat.matrices.dancing_links import dlx_solver149sage: rows = [[0,1,2]]150sage: X = dlx_solver(rows)151sage: X == loads(dumps(X))1521153sage: rows += [[2]]154sage: Y = dlx_solver(rows)155sage: Y == loads(dumps(X))1560157"""158return self.rows.dumps()159160def add_rows(self, rows):161"""162Initialize our instance of dancing_links with the given rows.163This is for internal use by dlx_solver.164165This doctest tests add_rows vicariously!166167TESTS:168sage: from sage.combinat.matrices.dancing_links import dlx_solver169sage: rows = [[0,1,2]]170sage: rows+= [[0,2]]171sage: rows+= [[1]]172sage: rows+= [[3]]173sage: x = dlx_solver(rows)174sage: print x.search()1751176"""177178cdef vector_int v179cdef vector_vector_int vv180181cdef int i = 0182183for row in rows:184v.clear()185186Py_INCREF(row);187PyList_SET_ITEM(self.rows, i, row)188i += 1189190for x in row:191v.push_back(x)192193vv.push_back(v)194195self.x.add_rows(vv)196197def get_solution(self):198"""199After calling search(), we can extract a solution200from the instance variable self.x.solution, a C++ vector<int>201listing the rows that make up the current solution.202203TESTS:204sage: from sage.combinat.matrices.dancing_links import dlx_solver205sage: rows = [[0,1,2]]206sage: rows+= [[0,2]]207sage: rows+= [[1]]208sage: rows+= [[3]]209sage: x = dlx_solver(rows)210sage: print x.search()2111212sage: print x.get_solution()213[3, 0]214"""215216s = []217for i in range(self.x.solution.size()):218s.append(self.x.solution.at(i))219220return s221222def search(self):223"""224TESTS:225sage: from sage.combinat.matrices.dancing_links import dlx_solver226sage: rows = [[0,1,2]]227sage: rows+= [[0,2]]228sage: rows+= [[1]]229sage: rows+= [[3]]230sage: x = dlx_solver(rows)231sage: print x.search()2321233sage: print x.get_solution()234[3, 0]235"""236237x = self.x.search()238239return x240241def dlx_solver(rows):242"""243Internal-use wrapper for the dancing links C++ code.244245EXAMPLES:246sage: from sage.combinat.matrices.dancing_links import dlx_solver247sage: rows = [[0,1,2]]248sage: rows+= [[0,2]]249sage: rows+= [[1]]250sage: rows+= [[3]]251sage: x = dlx_solver(rows)252sage: print x.search()2531254sage: print x.get_solution()255[3, 0]256sage: print x.search()2571258sage: print x.get_solution()259[3, 1, 2]260sage: print x.search()2610262"""263264cdef dancing_linksWrapper dlw265266dlw = dancing_linksWrapper(rows)267#dlw.add_rows(rows)268269return dlw270271272def make_dlxwrapper(s):273"""274Create a dlx wrapper from a Python *string* s.275This is used in unpickling. We expect s to be dumps(rows) where276rows is the list of rows used to instantiate the object.277278TESTS:279sage: rows = [[0,1,2]]280sage: x = make_dlxwrapper(dumps(rows))281sage: print x.__str__()282[[0, 1, 2]]283"""284285from sage.all import loads286287cdef dancing_linksWrapper dlw288dlw = dancing_linksWrapper(loads(s))289return dlw290291292293